4216043d9fa49795b9a515b5d68e8969d474e326
[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 "Completion.h"
30 #include "ConsoleMessage.h"
31 #include "ErrorHandlingScope.h"
32 #include "Exception.h"
33 #include "InjectedScriptHost.h"
34 #include "InjectedScriptManager.h"
35 #include "InspectorAgent.h"
36 #include "InspectorBackendDispatcher.h"
37 #include "InspectorFrontendChannel.h"
38 #include "InspectorFrontendRouter.h"
39 #include "InspectorHeapAgent.h"
40 #include "JSGlobalObject.h"
41 #include "JSGlobalObjectConsoleAgent.h"
42 #include "JSGlobalObjectConsoleClient.h"
43 #include "JSGlobalObjectDebuggerAgent.h"
44 #include "JSGlobalObjectRuntimeAgent.h"
45 #include "ScriptArguments.h"
46 #include "ScriptCallStack.h"
47 #include "ScriptCallStackFactory.h"
48 #include <wtf/Stopwatch.h>
49
50 #include <cxxabi.h>
51 #if OS(DARWIN) || (OS(LINUX) && !PLATFORM(GTK))
52 #include <dlfcn.h>
53 #include <execinfo.h>
54 #endif
55
56 #if ENABLE(REMOTE_INSPECTOR)
57 #include "JSGlobalObjectDebuggable.h"
58 #include "RemoteInspector.h"
59 #endif
60
61 using namespace JSC;
62
63 namespace Inspector {
64
65 JSGlobalObjectInspectorController::JSGlobalObjectInspectorController(JSGlobalObject& globalObject)
66     : m_globalObject(globalObject)
67     , m_injectedScriptManager(std::make_unique<InjectedScriptManager>(*this, InjectedScriptHost::create()))
68     , m_executionStopwatch(Stopwatch::create())
69     , m_frontendRouter(FrontendRouter::create())
70     , m_backendDispatcher(BackendDispatcher::create(m_frontendRouter.copyRef()))
71 {
72     AgentContext baseContext = {
73         *this,
74         *m_injectedScriptManager,
75         m_frontendRouter.get(),
76         m_backendDispatcher.get()
77     };
78
79     JSAgentContext context = {
80         baseContext,
81         globalObject
82     };
83
84     auto inspectorAgent = std::make_unique<InspectorAgent>(context);
85     auto runtimeAgent = std::make_unique<JSGlobalObjectRuntimeAgent>(context);
86     auto consoleAgent = std::make_unique<JSGlobalObjectConsoleAgent>(context);
87     auto debuggerAgent = std::make_unique<JSGlobalObjectDebuggerAgent>(context, consoleAgent.get());
88     auto heapAgent = std::make_unique<InspectorHeapAgent>(context);
89
90     m_inspectorAgent = inspectorAgent.get();
91     m_debuggerAgent = debuggerAgent.get();
92     m_heapAgent = heapAgent.get();
93     m_consoleAgent = consoleAgent.get();
94     m_consoleClient = std::make_unique<JSGlobalObjectConsoleClient>(m_consoleAgent);
95
96     runtimeAgent->setScriptDebugServer(&debuggerAgent->scriptDebugServer());
97
98     m_agents.append(WTF::move(inspectorAgent));
99     m_agents.append(WTF::move(runtimeAgent));
100     m_agents.append(WTF::move(consoleAgent));
101     m_agents.append(WTF::move(debuggerAgent));
102     m_agents.append(WTF::move(heapAgent));
103
104     m_executionStopwatch->start();
105 }
106
107 JSGlobalObjectInspectorController::~JSGlobalObjectInspectorController()
108 {
109 }
110
111 void JSGlobalObjectInspectorController::globalObjectDestroyed()
112 {
113     disconnectAllFrontends();
114
115     m_injectedScriptManager->disconnect();
116 }
117
118 void JSGlobalObjectInspectorController::connectFrontend(FrontendChannel* frontendChannel, bool isAutomaticInspection)
119 {
120     ASSERT_ARG(frontendChannel, frontendChannel);
121
122     m_isAutomaticInspection = isAutomaticInspection;
123
124     bool connectedFirstFrontend = !m_frontendRouter->hasFrontends();
125     m_frontendRouter->connectFrontend(frontendChannel);
126
127     if (!connectedFirstFrontend)
128         return;
129
130     // FIXME: change this to notify agents which frontend has connected (by id).
131     m_agents.didCreateFrontendAndBackend(nullptr, nullptr);
132
133 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
134     m_inspectorAgent->activateExtraDomains(m_agents.extraDomains());
135
136     if (m_augmentingClient)
137         m_augmentingClient->inspectorConnected();
138 #endif
139 }
140
141 void JSGlobalObjectInspectorController::disconnectFrontend(FrontendChannel* frontendChannel)
142 {
143     ASSERT_ARG(frontendChannel, frontendChannel);
144
145     // FIXME: change this to notify agents which frontend has disconnected (by id).
146     m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectorDestroyed);
147
148     m_frontendRouter->disconnectFrontend(frontendChannel);
149
150     m_isAutomaticInspection = false;
151
152     bool disconnectedLastFrontend = !m_frontendRouter->hasFrontends();
153     if (!disconnectedLastFrontend)
154         return;
155
156 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
157     if (m_augmentingClient)
158         m_augmentingClient->inspectorDisconnected();
159 #endif
160 }
161
162 void JSGlobalObjectInspectorController::disconnectAllFrontends()
163 {
164     // FIXME: change this to notify agents which frontend has disconnected (by id).
165     m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectedTargetDestroyed);
166
167     m_frontendRouter->disconnectAllFrontends();
168
169     m_isAutomaticInspection = false;
170
171 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
172     if (m_augmentingClient)
173         m_augmentingClient->inspectorDisconnected();
174 #endif
175 }
176
177 void JSGlobalObjectInspectorController::dispatchMessageFromFrontend(const String& message)
178 {
179     m_backendDispatcher->dispatch(message);
180 }
181
182 void JSGlobalObjectInspectorController::pause()
183 {
184     ErrorString dummyError;
185     m_debuggerAgent->enable(dummyError);
186     m_debuggerAgent->pause(dummyError);
187 }
188
189 void JSGlobalObjectInspectorController::appendAPIBacktrace(ScriptCallStack* callStack)
190 {
191 #if OS(DARWIN) || (OS(LINUX) && !PLATFORM(GTK))
192     static const int framesToShow = 31;
193     static const int framesToSkip = 3; // WTFGetBacktrace, appendAPIBacktrace, reportAPIException.
194
195     void* samples[framesToShow + framesToSkip];
196     int frames = framesToShow + framesToSkip;
197     WTFGetBacktrace(samples, &frames);
198
199     void** stack = samples + framesToSkip;
200     int size = frames - framesToSkip;
201     for (int i = 0; i < size; ++i) {
202         const char* mangledName = nullptr;
203         char* cxaDemangled = nullptr;
204         Dl_info info;
205         if (dladdr(stack[i], &info) && info.dli_sname)
206             mangledName = info.dli_sname;
207         if (mangledName)
208             cxaDemangled = abi::__cxa_demangle(mangledName, nullptr, nullptr, nullptr);
209         if (mangledName || cxaDemangled)
210             callStack->append(ScriptCallFrame(cxaDemangled ? cxaDemangled : mangledName, ASCIILiteral("[native code]"), 0, 0));
211         else
212             callStack->append(ScriptCallFrame(ASCIILiteral("?"), ASCIILiteral("[native code]"), 0, 0));
213         free(cxaDemangled);
214     }
215 #else
216     UNUSED_PARAM(callStack);
217 #endif
218 }
219
220 void JSGlobalObjectInspectorController::reportAPIException(ExecState* exec, Exception* exception)
221 {
222     if (isTerminatedExecutionException(exception))
223         return;
224
225     ErrorHandlingScope errorScope(exec->vm());
226
227     RefPtr<ScriptCallStack> callStack = createScriptCallStackFromException(exec, exception, ScriptCallStack::maxCallStackSizeToCapture);
228     if (includesNativeCallStackWhenReportingExceptions())
229         appendAPIBacktrace(callStack.get());
230
231     // FIXME: <http://webkit.org/b/115087> Web Inspector: Should not evaluate JavaScript handling exceptions
232     // If this is a custom exception object, call toString on it to try and get a nice string representation for the exception.
233     String errorMessage = exception->value().toString(exec)->value(exec);
234     exec->clearException();
235
236     if (JSGlobalObjectConsoleClient::logToSystemConsole()) {
237         if (callStack->size()) {
238             const ScriptCallFrame& callFrame = callStack->at(0);
239             ConsoleClient::printConsoleMessage(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, callFrame.sourceURL(), callFrame.lineNumber(), callFrame.columnNumber());
240         } else
241             ConsoleClient::printConsoleMessage(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, String(), 0, 0);
242     }
243
244     m_consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, callStack));
245 }
246
247 ConsoleClient* JSGlobalObjectInspectorController::consoleClient() const
248 {
249     return m_consoleClient.get();
250 }
251
252 bool JSGlobalObjectInspectorController::developerExtrasEnabled() const
253 {
254 #if ENABLE(REMOTE_INSPECTOR)
255     if (!RemoteInspector::singleton().enabled())
256         return false;
257
258     if (!m_globalObject.inspectorDebuggable().remoteDebuggingAllowed())
259         return false;
260 #endif
261
262     return true;
263 }
264
265 InspectorFunctionCallHandler JSGlobalObjectInspectorController::functionCallHandler() const
266 {
267     return JSC::call;
268 }
269
270 InspectorEvaluateHandler JSGlobalObjectInspectorController::evaluateHandler() const
271 {
272     return JSC::evaluate;
273 }
274
275 void JSGlobalObjectInspectorController::frontendInitialized()
276 {
277 #if ENABLE(REMOTE_INSPECTOR)
278     if (m_isAutomaticInspection)
279         m_globalObject.inspectorDebuggable().unpauseForInitializedInspector();
280 #endif
281 }
282
283 Ref<Stopwatch> JSGlobalObjectInspectorController::executionStopwatch()
284 {
285     return m_executionStopwatch.copyRef();
286 }
287
288 VM& JSGlobalObjectInspectorController::vm()
289 {
290     return m_globalObject.vm();
291 }
292
293 #if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
294 void JSGlobalObjectInspectorController::appendExtraAgent(std::unique_ptr<InspectorAgentBase> agent)
295 {
296     String domainName = agent->domainName();
297
298     // FIXME: change this to notify agents which frontend has connected (by id).
299     agent->didCreateFrontendAndBackend(nullptr, nullptr);
300
301     m_agents.appendExtraAgent(WTF::move(agent));
302
303     m_inspectorAgent->activateExtraDomain(domainName);
304 }
305 #endif
306
307 } // namespace Inspector
308