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