Source/JavaScriptCore:
[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 consoleAgent = std::make_unique<InspectorConsoleAgent>(context);
75     m_consoleAgent = consoleAgent.get();
76     m_agents.append(WTFMove(consoleAgent));
77
78     m_consoleClient = std::make_unique<JSGlobalObjectConsoleClient>(m_consoleAgent);
79
80     m_executionStopwatch->start();
81 }
82
83 JSGlobalObjectInspectorController::~JSGlobalObjectInspectorController()
84 {
85 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
86     if (m_augmentingClient)
87         m_augmentingClient->inspectorControllerDestroyed();
88 #endif
89 }
90
91 void JSGlobalObjectInspectorController::globalObjectDestroyed()
92 {
93     ASSERT(!m_frontendRouter->hasFrontends());
94
95     m_injectedScriptManager->disconnect();
96
97     m_agents.discardValues();
98 }
99
100 void JSGlobalObjectInspectorController::connectFrontend(FrontendChannel& frontendChannel, bool isAutomaticInspection, bool immediatelyPause)
101 {
102     m_isAutomaticInspection = isAutomaticInspection;
103     m_pauseAfterInitialization = immediatelyPause;
104
105     createLazyAgents();
106
107     bool connectedFirstFrontend = !m_frontendRouter->hasFrontends();
108     m_frontendRouter->connectFrontend(frontendChannel);
109
110     if (!connectedFirstFrontend)
111         return;
112
113     // Keep the JSGlobalObject and VM alive while we are debugging it.
114     m_strongVM = &m_globalObject.vm();
115     m_strongGlobalObject.set(m_globalObject.vm(), &m_globalObject);
116
117     // FIXME: change this to notify agents which frontend has connected (by id).
118     m_agents.didCreateFrontendAndBackend(nullptr, nullptr);
119
120 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
121     ASSERT(m_inspectorAgent);
122     m_inspectorAgent->activateExtraDomains(m_agents.extraDomains());
123
124     if (m_augmentingClient)
125         m_augmentingClient->inspectorConnected();
126 #endif
127 }
128
129 void JSGlobalObjectInspectorController::disconnectFrontend(FrontendChannel& frontendChannel)
130 {
131     // FIXME: change this to notify agents which frontend has disconnected (by id).
132     m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectorDestroyed);
133
134     m_frontendRouter->disconnectFrontend(frontendChannel);
135
136     m_isAutomaticInspection = false;
137     m_pauseAfterInitialization = false;
138
139     bool disconnectedLastFrontend = !m_frontendRouter->hasFrontends();
140     if (!disconnectedLastFrontend)
141         return;
142
143 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
144     if (m_augmentingClient)
145         m_augmentingClient->inspectorDisconnected();
146 #endif
147
148     // Remove our JSGlobalObject and VM references, we are done debugging it.
149     m_strongGlobalObject.clear();
150     m_strongVM = nullptr;
151 }
152
153 void JSGlobalObjectInspectorController::dispatchMessageFromFrontend(const String& message)
154 {
155     m_backendDispatcher->dispatch(message);
156 }
157
158 void JSGlobalObjectInspectorController::appendAPIBacktrace(ScriptCallStack& callStack)
159 {
160     static const int framesToShow = 31;
161     static const int framesToSkip = 3; // WTFGetBacktrace, appendAPIBacktrace, reportAPIException.
162
163     void* samples[framesToShow + framesToSkip];
164     int frames = framesToShow + framesToSkip;
165     WTFGetBacktrace(samples, &frames);
166
167     void** stack = samples + framesToSkip;
168     int size = frames - framesToSkip;
169     for (int i = 0; i < size; ++i) {
170         auto demangled = StackTrace::demangle(stack[i]);
171         if (demangled)
172             callStack.append(ScriptCallFrame(demangled->demangledName() ? demangled->demangledName() : demangled->mangledName(), "[native code]"_s, noSourceID, 0, 0));
173         else
174             callStack.append(ScriptCallFrame("?"_s, "[native code]"_s, noSourceID, 0, 0));
175     }
176 }
177
178 void JSGlobalObjectInspectorController::reportAPIException(ExecState* exec, Exception* exception)
179 {
180     VM& vm = exec->vm();
181     if (isTerminatedExecutionException(vm, exception))
182         return;
183
184     auto scope = DECLARE_CATCH_SCOPE(vm);
185     ErrorHandlingScope errorScope(vm);
186
187     Ref<ScriptCallStack> callStack = createScriptCallStackFromException(exec, exception);
188     if (includesNativeCallStackWhenReportingExceptions())
189         appendAPIBacktrace(callStack.get());
190
191     // FIXME: <http://webkit.org/b/115087> Web Inspector: Should not evaluate JavaScript handling exceptions
192     // If this is a custom exception object, call toString on it to try and get a nice string representation for the exception.
193     String errorMessage = exception->value().toWTFString(exec);
194     scope.clearException();
195
196     if (JSGlobalObjectConsoleClient::logToSystemConsole()) {
197         if (callStack->size()) {
198             const ScriptCallFrame& callFrame = callStack->at(0);
199             ConsoleClient::printConsoleMessage(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, callFrame.sourceURL(), callFrame.lineNumber(), callFrame.columnNumber());
200         } else
201             ConsoleClient::printConsoleMessage(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, String(), 0, 0);
202     }
203
204     m_consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, WTFMove(callStack)));
205 }
206
207 ConsoleClient* JSGlobalObjectInspectorController::consoleClient() const
208 {
209     return m_consoleClient.get();
210 }
211
212 bool JSGlobalObjectInspectorController::developerExtrasEnabled() const
213 {
214 #if ENABLE(REMOTE_INSPECTOR)
215     if (!RemoteInspector::singleton().enabled())
216         return false;
217
218     if (!m_globalObject.inspectorDebuggable().remoteDebuggingAllowed())
219         return false;
220 #endif
221
222     return true;
223 }
224
225 InspectorFunctionCallHandler JSGlobalObjectInspectorController::functionCallHandler() const
226 {
227     return JSC::call;
228 }
229
230 InspectorEvaluateHandler JSGlobalObjectInspectorController::evaluateHandler() const
231 {
232     return JSC::evaluate;
233 }
234
235 void JSGlobalObjectInspectorController::frontendInitialized()
236 {
237     if (m_pauseAfterInitialization) {
238         m_pauseAfterInitialization = false;
239
240         ASSERT(m_debuggerAgent);
241         ErrorString ignored;
242         m_debuggerAgent->enable(ignored);
243         m_debuggerAgent->pause(ignored);
244     }
245
246 #if ENABLE(REMOTE_INSPECTOR)
247     if (m_isAutomaticInspection)
248         m_globalObject.inspectorDebuggable().unpauseForInitializedInspector();
249 #endif
250 }
251
252 Ref<Stopwatch> JSGlobalObjectInspectorController::executionStopwatch()
253 {
254     return m_executionStopwatch.copyRef();
255 }
256
257 JSGlobalObjectScriptDebugServer& JSGlobalObjectInspectorController::scriptDebugServer()
258 {
259     return m_scriptDebugServer;
260 }
261
262 VM& JSGlobalObjectInspectorController::vm()
263 {
264     return m_globalObject.vm();
265 }
266
267 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
268 void JSGlobalObjectInspectorController::appendExtraAgent(std::unique_ptr<InspectorAgentBase> agent)
269 {
270     String domainName = agent->domainName();
271
272     // FIXME: change this to notify agents which frontend has connected (by id).
273     agent->didCreateFrontendAndBackend(nullptr, nullptr);
274
275     m_agents.appendExtraAgent(WTFMove(agent));
276
277     ASSERT(m_inspectorAgent);
278     m_inspectorAgent->activateExtraDomain(domainName);
279 }
280 #endif
281
282 JSAgentContext JSGlobalObjectInspectorController::jsAgentContext()
283 {
284     AgentContext baseContext = {
285         *this,
286         *m_injectedScriptManager,
287         m_frontendRouter.get(),
288         m_backendDispatcher.get()
289     };
290
291     JSAgentContext context = {
292         baseContext,
293         m_globalObject
294     };
295
296     return context;
297 }
298
299 void JSGlobalObjectInspectorController::createLazyAgents()
300 {
301     if (m_didCreateLazyAgents)
302         return;
303
304     m_didCreateLazyAgents = true;
305
306     auto context = jsAgentContext();
307
308     auto inspectorAgent = std::make_unique<InspectorAgent>(context);
309     m_inspectorAgent = inspectorAgent.get();
310     m_agents.append(WTFMove(inspectorAgent));
311
312     m_agents.append(std::make_unique<JSGlobalObjectRuntimeAgent>(context));
313
314     auto debuggerAgent = std::make_unique<JSGlobalObjectDebuggerAgent>(context, m_consoleAgent);
315     m_debuggerAgent = debuggerAgent.get();
316     m_consoleClient->setInspectorDebuggerAgent(m_debuggerAgent);
317     m_agents.append(WTFMove(debuggerAgent));
318
319     auto scriptProfilerAgentPtr = std::make_unique<InspectorScriptProfilerAgent>(context);
320     m_consoleClient->setInspectorScriptProfilerAgent(scriptProfilerAgentPtr.get());
321     m_agents.append(WTFMove(scriptProfilerAgentPtr));
322
323     auto heapAgent = std::make_unique<InspectorHeapAgent>(context);
324     if (m_consoleAgent)
325         m_consoleAgent->setInspectorHeapAgent(heapAgent.get());
326     m_agents.append(WTFMove(heapAgent));
327
328     m_agents.append(std::make_unique<JSGlobalObjectAuditAgent>(context));
329 }
330
331 } // namespace Inspector