Web Inspector: Heap: lazily create the agent
[WebKit-https.git] / Source / JavaScriptCore / inspector / JSGlobalObjectInspectorController.cpp
1 /*
2  * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JSGlobalObjectInspectorController.h"
28
29 #include "CatchScope.h"
30 #include "Completion.h"
31 #include "ConsoleMessage.h"
32 #include "ErrorHandlingScope.h"
33 #include "Exception.h"
34 #include "InjectedScriptHost.h"
35 #include "InjectedScriptManager.h"
36 #include "InspectorAgent.h"
37 #include "InspectorBackendDispatcher.h"
38 #include "InspectorConsoleAgent.h"
39 #include "InspectorFrontendChannel.h"
40 #include "InspectorFrontendRouter.h"
41 #include "InspectorHeapAgent.h"
42 #include "InspectorScriptProfilerAgent.h"
43 #include "JSCInlines.h"
44 #include "JSGlobalObject.h"
45 #include "JSGlobalObjectAuditAgent.h"
46 #include "JSGlobalObjectConsoleClient.h"
47 #include "JSGlobalObjectDebuggerAgent.h"
48 #include "JSGlobalObjectRuntimeAgent.h"
49 #include "ScriptArguments.h"
50 #include "ScriptCallStack.h"
51 #include "ScriptCallStackFactory.h"
52 #include <wtf/StackTrace.h>
53 #include <wtf/Stopwatch.h>
54
55 #if ENABLE(REMOTE_INSPECTOR)
56 #include "JSGlobalObjectDebuggable.h"
57 #include "RemoteInspector.h"
58 #endif
59
60 using namespace JSC;
61
62 namespace Inspector {
63
64 JSGlobalObjectInspectorController::JSGlobalObjectInspectorController(JSGlobalObject& globalObject)
65     : m_globalObject(globalObject)
66     , m_injectedScriptManager(std::make_unique<InjectedScriptManager>(*this, InjectedScriptHost::create()))
67     , m_executionStopwatch(Stopwatch::create())
68     , m_scriptDebugServer(globalObject)
69     , m_frontendRouter(FrontendRouter::create())
70     , m_backendDispatcher(BackendDispatcher::create(m_frontendRouter.copyRef()))
71 {
72     auto context = jsAgentContext();
73
74     auto inspectorAgent = std::make_unique<InspectorAgent>(context);
75     auto runtimeAgent = std::make_unique<JSGlobalObjectRuntimeAgent>(context);
76     auto consoleAgent = std::make_unique<InspectorConsoleAgent>(context);
77     auto debuggerAgent = std::make_unique<JSGlobalObjectDebuggerAgent>(context, consoleAgent.get());
78     auto scriptProfilerAgent = std::make_unique<InspectorScriptProfilerAgent>(context);
79
80     m_inspectorAgent = inspectorAgent.get();
81     m_debuggerAgent = debuggerAgent.get();
82     m_consoleAgent = consoleAgent.get();
83     m_consoleClient = std::make_unique<JSGlobalObjectConsoleClient>(m_consoleAgent, m_debuggerAgent, scriptProfilerAgent.get());
84
85     m_agents.append(WTFMove(inspectorAgent));
86     m_agents.append(WTFMove(runtimeAgent));
87     m_agents.append(WTFMove(consoleAgent));
88     m_agents.append(WTFMove(debuggerAgent));
89     m_agents.append(WTFMove(scriptProfilerAgent));
90
91     m_executionStopwatch->start();
92 }
93
94 JSGlobalObjectInspectorController::~JSGlobalObjectInspectorController()
95 {
96 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
97     if (m_augmentingClient)
98         m_augmentingClient->inspectorControllerDestroyed();
99 #endif
100 }
101
102 void JSGlobalObjectInspectorController::globalObjectDestroyed()
103 {
104     ASSERT(!m_frontendRouter->hasFrontends());
105
106     m_injectedScriptManager->disconnect();
107
108     m_agents.discardValues();
109 }
110
111 void JSGlobalObjectInspectorController::connectFrontend(FrontendChannel& frontendChannel, bool isAutomaticInspection, bool immediatelyPause)
112 {
113     m_isAutomaticInspection = isAutomaticInspection;
114     m_pauseAfterInitialization = immediatelyPause;
115
116     createLazyAgents();
117
118     bool connectedFirstFrontend = !m_frontendRouter->hasFrontends();
119     m_frontendRouter->connectFrontend(frontendChannel);
120
121     if (!connectedFirstFrontend)
122         return;
123
124     // Keep the JSGlobalObject and VM alive while we are debugging it.
125     m_strongVM = &m_globalObject.vm();
126     m_strongGlobalObject.set(m_globalObject.vm(), &m_globalObject);
127
128     // FIXME: change this to notify agents which frontend has connected (by id).
129     m_agents.didCreateFrontendAndBackend(nullptr, nullptr);
130
131 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
132     m_inspectorAgent->activateExtraDomains(m_agents.extraDomains());
133
134     if (m_augmentingClient)
135         m_augmentingClient->inspectorConnected();
136 #endif
137 }
138
139 void JSGlobalObjectInspectorController::disconnectFrontend(FrontendChannel& frontendChannel)
140 {
141     // FIXME: change this to notify agents which frontend has disconnected (by id).
142     m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectorDestroyed);
143
144     m_frontendRouter->disconnectFrontend(frontendChannel);
145
146     m_isAutomaticInspection = false;
147     m_pauseAfterInitialization = false;
148
149     bool disconnectedLastFrontend = !m_frontendRouter->hasFrontends();
150     if (!disconnectedLastFrontend)
151         return;
152
153 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
154     if (m_augmentingClient)
155         m_augmentingClient->inspectorDisconnected();
156 #endif
157
158     // Remove our JSGlobalObject and VM references, we are done debugging it.
159     m_strongGlobalObject.clear();
160     m_strongVM = nullptr;
161 }
162
163 void JSGlobalObjectInspectorController::dispatchMessageFromFrontend(const String& message)
164 {
165     m_backendDispatcher->dispatch(message);
166 }
167
168 void JSGlobalObjectInspectorController::appendAPIBacktrace(ScriptCallStack& callStack)
169 {
170     static const int framesToShow = 31;
171     static const int framesToSkip = 3; // WTFGetBacktrace, appendAPIBacktrace, reportAPIException.
172
173     void* samples[framesToShow + framesToSkip];
174     int frames = framesToShow + framesToSkip;
175     WTFGetBacktrace(samples, &frames);
176
177     void** stack = samples + framesToSkip;
178     int size = frames - framesToSkip;
179     for (int i = 0; i < size; ++i) {
180         auto demangled = StackTrace::demangle(stack[i]);
181         if (demangled)
182             callStack.append(ScriptCallFrame(demangled->demangledName() ? demangled->demangledName() : demangled->mangledName(), "[native code]"_s, noSourceID, 0, 0));
183         else
184             callStack.append(ScriptCallFrame("?"_s, "[native code]"_s, noSourceID, 0, 0));
185     }
186 }
187
188 void JSGlobalObjectInspectorController::reportAPIException(ExecState* exec, Exception* exception)
189 {
190     VM& vm = exec->vm();
191     if (isTerminatedExecutionException(vm, exception))
192         return;
193
194     auto scope = DECLARE_CATCH_SCOPE(vm);
195     ErrorHandlingScope errorScope(vm);
196
197     Ref<ScriptCallStack> callStack = createScriptCallStackFromException(exec, exception);
198     if (includesNativeCallStackWhenReportingExceptions())
199         appendAPIBacktrace(callStack.get());
200
201     // FIXME: <http://webkit.org/b/115087> Web Inspector: Should not evaluate JavaScript handling exceptions
202     // If this is a custom exception object, call toString on it to try and get a nice string representation for the exception.
203     String errorMessage = exception->value().toWTFString(exec);
204     scope.clearException();
205
206     if (JSGlobalObjectConsoleClient::logToSystemConsole()) {
207         if (callStack->size()) {
208             const ScriptCallFrame& callFrame = callStack->at(0);
209             ConsoleClient::printConsoleMessage(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, callFrame.sourceURL(), callFrame.lineNumber(), callFrame.columnNumber());
210         } else
211             ConsoleClient::printConsoleMessage(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, String(), 0, 0);
212     }
213
214     m_consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, WTFMove(callStack)));
215 }
216
217 ConsoleClient* JSGlobalObjectInspectorController::consoleClient() const
218 {
219     return m_consoleClient.get();
220 }
221
222 bool JSGlobalObjectInspectorController::developerExtrasEnabled() const
223 {
224 #if ENABLE(REMOTE_INSPECTOR)
225     if (!RemoteInspector::singleton().enabled())
226         return false;
227
228     if (!m_globalObject.inspectorDebuggable().remoteDebuggingAllowed())
229         return false;
230 #endif
231
232     return true;
233 }
234
235 InspectorFunctionCallHandler JSGlobalObjectInspectorController::functionCallHandler() const
236 {
237     return JSC::call;
238 }
239
240 InspectorEvaluateHandler JSGlobalObjectInspectorController::evaluateHandler() const
241 {
242     return JSC::evaluate;
243 }
244
245 void JSGlobalObjectInspectorController::frontendInitialized()
246 {
247     if (m_pauseAfterInitialization) {
248         m_pauseAfterInitialization = false;
249         ErrorString ignored;
250         m_debuggerAgent->enable(ignored);
251         m_debuggerAgent->pause(ignored);
252     }
253
254 #if ENABLE(REMOTE_INSPECTOR)
255     if (m_isAutomaticInspection)
256         m_globalObject.inspectorDebuggable().unpauseForInitializedInspector();
257 #endif
258 }
259
260 Ref<Stopwatch> JSGlobalObjectInspectorController::executionStopwatch()
261 {
262     return m_executionStopwatch.copyRef();
263 }
264
265 JSGlobalObjectScriptDebugServer& JSGlobalObjectInspectorController::scriptDebugServer()
266 {
267     return m_scriptDebugServer;
268 }
269
270 VM& JSGlobalObjectInspectorController::vm()
271 {
272     return m_globalObject.vm();
273 }
274
275 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
276 void JSGlobalObjectInspectorController::appendExtraAgent(std::unique_ptr<InspectorAgentBase> agent)
277 {
278     String domainName = agent->domainName();
279
280     // FIXME: change this to notify agents which frontend has connected (by id).
281     agent->didCreateFrontendAndBackend(nullptr, nullptr);
282
283     m_agents.appendExtraAgent(WTFMove(agent));
284
285     m_inspectorAgent->activateExtraDomain(domainName);
286 }
287 #endif
288
289 JSAgentContext JSGlobalObjectInspectorController::jsAgentContext()
290 {
291     AgentContext baseContext = {
292         *this,
293         *m_injectedScriptManager,
294         m_frontendRouter.get(),
295         m_backendDispatcher.get()
296     };
297
298     JSAgentContext context = {
299         baseContext,
300         m_globalObject
301     };
302
303     return context;
304 }
305
306 void JSGlobalObjectInspectorController::createLazyAgents()
307 {
308     if (m_didCreateLazyAgents)
309         return;
310
311     m_didCreateLazyAgents = true;
312
313     auto context = jsAgentContext();
314
315     auto heapAgent = std::make_unique<InspectorHeapAgent>(context);
316     if (m_consoleAgent)
317         m_consoleAgent->setInspectorHeapAgent(heapAgent.get());
318     m_agents.append(WTFMove(heapAgent));
319
320     m_agents.append(std::make_unique<JSGlobalObjectAuditAgent>(context));
321 }
322
323 } // namespace Inspector