1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "InspectorController.h"
34
35#include "CommandLineAPIHost.h"
36#include "CommonVM.h"
37#include "DOMWindow.h"
38#include "DOMWrapperWorld.h"
39#include "Frame.h"
40#include "GraphicsContext.h"
41#include "InspectorApplicationCacheAgent.h"
42#include "InspectorCPUProfilerAgent.h"
43#include "InspectorCSSAgent.h"
44#include "InspectorCanvasAgent.h"
45#include "InspectorClient.h"
46#include "InspectorDOMAgent.h"
47#include "InspectorDOMDebuggerAgent.h"
48#include "InspectorDOMStorageAgent.h"
49#include "InspectorDatabaseAgent.h"
50#include "InspectorDatabaseResource.h"
51#include "InspectorFrontendClient.h"
52#include "InspectorIndexedDBAgent.h"
53#include "InspectorInstrumentation.h"
54#include "InspectorLayerTreeAgent.h"
55#include "InspectorMemoryAgent.h"
56#include "InspectorPageAgent.h"
57#include "InspectorTimelineAgent.h"
58#include "InspectorWorkerAgent.h"
59#include "InstrumentingAgents.h"
60#include "JSDOMBindingSecurity.h"
61#include "JSDOMWindow.h"
62#include "JSDOMWindowCustom.h"
63#include "JSExecState.h"
64#include "Page.h"
65#include "PageAuditAgent.h"
66#include "PageConsoleAgent.h"
67#include "PageDebuggerAgent.h"
68#include "PageHeapAgent.h"
69#include "PageNetworkAgent.h"
70#include "PageRuntimeAgent.h"
71#include "PageScriptDebugServer.h"
72#include "Settings.h"
73#include "WebInjectedScriptHost.h"
74#include "WebInjectedScriptManager.h"
75#include <JavaScriptCore/IdentifiersFactory.h>
76#include <JavaScriptCore/InspectorAgent.h>
77#include <JavaScriptCore/InspectorBackendDispatcher.h>
78#include <JavaScriptCore/InspectorBackendDispatchers.h>
79#include <JavaScriptCore/InspectorFrontendDispatchers.h>
80#include <JavaScriptCore/InspectorFrontendRouter.h>
81#include <JavaScriptCore/InspectorScriptProfilerAgent.h>
82#include <JavaScriptCore/JSLock.h>
83#include <wtf/Stopwatch.h>
84
85#if ENABLE(REMOTE_INSPECTOR)
86#include "PageDebuggable.h"
87#endif
88
89namespace WebCore {
90
91using namespace JSC;
92using namespace Inspector;
93
94InspectorController::InspectorController(Page& page, InspectorClient* inspectorClient)
95 : m_instrumentingAgents(InstrumentingAgents::create(*this))
96 , m_injectedScriptManager(std::make_unique<WebInjectedScriptManager>(*this, WebInjectedScriptHost::create()))
97 , m_frontendRouter(FrontendRouter::create())
98 , m_backendDispatcher(BackendDispatcher::create(m_frontendRouter.copyRef()))
99 , m_overlay(std::make_unique<InspectorOverlay>(page, inspectorClient))
100 , m_executionStopwatch(Stopwatch::create())
101 , m_scriptDebugServer(page)
102 , m_page(page)
103 , m_inspectorClient(inspectorClient)
104{
105 ASSERT_ARG(inspectorClient, inspectorClient);
106
107 auto pageContext = pageAgentContext();
108
109 auto consoleAgent = std::make_unique<PageConsoleAgent>(pageContext);
110 m_instrumentingAgents->setWebConsoleAgent(consoleAgent.get());
111 m_agents.append(WTFMove(consoleAgent));
112
113 ASSERT(m_injectedScriptManager->commandLineAPIHost());
114 if (auto* commandLineAPIHost = m_injectedScriptManager->commandLineAPIHost())
115 commandLineAPIHost->init(m_instrumentingAgents.copyRef());
116}
117
118InspectorController::~InspectorController()
119{
120 m_instrumentingAgents->reset();
121 ASSERT(!m_inspectorClient);
122}
123
124PageAgentContext InspectorController::pageAgentContext()
125{
126 AgentContext baseContext = {
127 *this,
128 *m_injectedScriptManager,
129 m_frontendRouter.get(),
130 m_backendDispatcher.get()
131 };
132
133 WebAgentContext webContext = {
134 baseContext,
135 m_instrumentingAgents.get()
136 };
137
138 PageAgentContext pageContext = {
139 webContext,
140 m_page
141 };
142
143 return pageContext;
144}
145
146void InspectorController::createLazyAgents()
147{
148 if (m_didCreateLazyAgents)
149 return;
150
151 m_didCreateLazyAgents = true;
152
153 auto pageContext = pageAgentContext();
154
155 ensureInspectorAgent();
156 ensurePageAgent();
157
158 m_agents.append(std::make_unique<PageRuntimeAgent>(pageContext));
159
160 auto debuggerAgent = std::make_unique<PageDebuggerAgent>(pageContext);
161 auto debuggerAgentPtr = debuggerAgent.get();
162 m_agents.append(WTFMove(debuggerAgent));
163
164 m_agents.append(std::make_unique<PageNetworkAgent>(pageContext));
165 m_agents.append(std::make_unique<InspectorCSSAgent>(pageContext));
166 ensureDOMAgent();
167 m_agents.append(std::make_unique<InspectorDOMDebuggerAgent>(pageContext, debuggerAgentPtr));
168 m_agents.append(std::make_unique<InspectorApplicationCacheAgent>(pageContext));
169 m_agents.append(std::make_unique<InspectorLayerTreeAgent>(pageContext));
170 m_agents.append(std::make_unique<InspectorWorkerAgent>(pageContext));
171 m_agents.append(std::make_unique<InspectorDOMStorageAgent>(pageContext));
172 m_agents.append(std::make_unique<InspectorDatabaseAgent>(pageContext));
173#if ENABLE(INDEXED_DATABASE)
174 m_agents.append(std::make_unique<InspectorIndexedDBAgent>(pageContext));
175#endif
176
177 auto scriptProfilerAgentPtr = std::make_unique<InspectorScriptProfilerAgent>(pageContext);
178 m_instrumentingAgents->setInspectorScriptProfilerAgent(scriptProfilerAgentPtr.get());
179 m_agents.append(WTFMove(scriptProfilerAgentPtr));
180
181#if ENABLE(RESOURCE_USAGE)
182 m_agents.append(std::make_unique<InspectorCPUProfilerAgent>(pageContext));
183 m_agents.append(std::make_unique<InspectorMemoryAgent>(pageContext));
184#endif
185 m_agents.append(std::make_unique<PageHeapAgent>(pageContext));
186 m_agents.append(std::make_unique<PageAuditAgent>(pageContext));
187 m_agents.append(std::make_unique<InspectorCanvasAgent>(pageContext));
188 m_agents.append(std::make_unique<InspectorTimelineAgent>(pageContext));
189}
190
191void InspectorController::inspectedPageDestroyed()
192{
193 // Clean up resources and disconnect local and remote frontends.
194 disconnectAllFrontends();
195
196 // Disconnect the client.
197 m_inspectorClient->inspectedPageDestroyed();
198 m_inspectorClient = nullptr;
199
200 m_agents.discardValues();
201}
202
203void InspectorController::setInspectorFrontendClient(InspectorFrontendClient* inspectorFrontendClient)
204{
205 m_inspectorFrontendClient = inspectorFrontendClient;
206}
207
208bool InspectorController::hasLocalFrontend() const
209{
210 return m_frontendRouter->hasLocalFrontend();
211}
212
213bool InspectorController::hasRemoteFrontend() const
214{
215 return m_frontendRouter->hasRemoteFrontend();
216}
217
218unsigned InspectorController::inspectionLevel() const
219{
220 return m_inspectorFrontendClient ? m_inspectorFrontendClient->inspectionLevel() : 0;
221}
222
223void InspectorController::didClearWindowObjectInWorld(Frame& frame, DOMWrapperWorld& world)
224{
225 if (&world != &mainThreadNormalWorld())
226 return;
227
228 if (frame.isMainFrame())
229 m_injectedScriptManager->discardInjectedScripts();
230
231 // If the page is supposed to serve as InspectorFrontend notify inspector frontend
232 // client that it's cleared so that the client can expose inspector bindings.
233 if (m_inspectorFrontendClient && frame.isMainFrame())
234 m_inspectorFrontendClient->windowObjectCleared();
235}
236
237void InspectorController::connectFrontend(Inspector::FrontendChannel& frontendChannel, bool isAutomaticInspection, bool immediatelyPause)
238{
239 ASSERT(m_inspectorClient);
240
241 // If a frontend has connected enable the developer extras and keep them enabled.
242 m_page.settings().setDeveloperExtrasEnabled(true);
243
244 createLazyAgents();
245
246 bool connectedFirstFrontend = !m_frontendRouter->hasFrontends();
247 m_isAutomaticInspection = isAutomaticInspection;
248 m_pauseAfterInitialization = immediatelyPause;
249
250 m_frontendRouter->connectFrontend(frontendChannel);
251
252 InspectorInstrumentation::frontendCreated();
253
254 if (connectedFirstFrontend) {
255 InspectorInstrumentation::registerInstrumentingAgents(m_instrumentingAgents.get());
256 m_agents.didCreateFrontendAndBackend(&m_frontendRouter.get(), &m_backendDispatcher.get());
257 }
258
259 m_inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount());
260
261#if ENABLE(REMOTE_INSPECTOR)
262 if (hasLocalFrontend())
263 m_page.remoteInspectorInformationDidChange();
264#endif
265}
266
267void InspectorController::disconnectFrontend(FrontendChannel& frontendChannel)
268{
269 m_frontendRouter->disconnectFrontend(frontendChannel);
270
271 m_isAutomaticInspection = false;
272 m_pauseAfterInitialization = false;
273
274 InspectorInstrumentation::frontendDeleted();
275
276 bool disconnectedLastFrontend = !m_frontendRouter->hasFrontends();
277 if (disconnectedLastFrontend) {
278 // Notify agents first.
279 m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectorDestroyed);
280
281 // Clean up inspector resources.
282 m_injectedScriptManager->discardInjectedScripts();
283
284 // Unplug all instrumentations since they aren't needed now.
285 InspectorInstrumentation::unregisterInstrumentingAgents(m_instrumentingAgents.get());
286 }
287
288 m_inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount());
289
290#if ENABLE(REMOTE_INSPECTOR)
291 if (disconnectedLastFrontend)
292 m_page.remoteInspectorInformationDidChange();
293#endif
294}
295
296void InspectorController::disconnectAllFrontends()
297{
298 // If the local frontend page was destroyed, close the window.
299 if (m_inspectorFrontendClient)
300 m_inspectorFrontendClient->closeWindow();
301
302 // The frontend should call setInspectorFrontendClient(nullptr) under closeWindow().
303 ASSERT(!m_inspectorFrontendClient);
304
305 if (!m_frontendRouter->hasFrontends())
306 return;
307
308 for (unsigned i = 0; i < m_frontendRouter->frontendCount(); ++i)
309 InspectorInstrumentation::frontendDeleted();
310
311 // Unplug all instrumentations to prevent further agent callbacks.
312 InspectorInstrumentation::unregisterInstrumentingAgents(m_instrumentingAgents.get());
313
314 // Notify agents first, since they may need to use InspectorClient.
315 m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectedTargetDestroyed);
316
317 // Clean up inspector resources.
318 m_injectedScriptManager->disconnect();
319
320 // Disconnect any remaining remote frontends.
321 m_frontendRouter->disconnectAllFrontends();
322 m_isAutomaticInspection = false;
323 m_pauseAfterInitialization = false;
324
325 m_inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount());
326
327#if ENABLE(REMOTE_INSPECTOR)
328 m_page.remoteInspectorInformationDidChange();
329#endif
330}
331
332void InspectorController::show()
333{
334 ASSERT(!hasRemoteFrontend());
335
336 if (!enabled())
337 return;
338
339 if (m_frontendRouter->hasLocalFrontend())
340 m_inspectorClient->bringFrontendToFront();
341 else if (Inspector::FrontendChannel* frontendChannel = m_inspectorClient->openLocalFrontend(this))
342 connectFrontend(*frontendChannel);
343}
344
345void InspectorController::setIsUnderTest(bool value)
346{
347 if (value == m_isUnderTest)
348 return;
349
350 m_isUnderTest = value;
351
352 // <rdar://problem/26768628> Try to catch suspicious scenarios where we may have a dangling frontend while running tests.
353 RELEASE_ASSERT(!m_isUnderTest || !m_frontendRouter->hasFrontends());
354}
355
356void InspectorController::evaluateForTestInFrontend(const String& script)
357{
358 ensureInspectorAgent().evaluateForTestInFrontend(script);
359}
360
361void InspectorController::drawHighlight(GraphicsContext& context) const
362{
363 m_overlay->paint(context);
364}
365
366void InspectorController::getHighlight(Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem) const
367{
368 m_overlay->getHighlight(highlight, coordinateSystem);
369}
370
371void InspectorController::inspect(Node* node)
372{
373 if (!enabled())
374 return;
375
376 if (!hasRemoteFrontend())
377 show();
378
379 ensureDOMAgent().inspect(node);
380}
381
382bool InspectorController::enabled() const
383{
384 return developerExtrasEnabled();
385}
386
387Page& InspectorController::inspectedPage() const
388{
389 return m_page;
390}
391
392void InspectorController::dispatchMessageFromFrontend(const String& message)
393{
394 m_backendDispatcher->dispatch(message);
395}
396
397void InspectorController::hideHighlight()
398{
399 m_overlay->hideHighlight();
400}
401
402Node* InspectorController::highlightedNode() const
403{
404 return m_overlay->highlightedNode();
405}
406
407void InspectorController::setIndicating(bool indicating)
408{
409#if !PLATFORM(IOS_FAMILY)
410 m_overlay->setIndicating(indicating);
411#else
412 if (indicating)
413 m_inspectorClient->showInspectorIndication();
414 else
415 m_inspectorClient->hideInspectorIndication();
416#endif
417}
418
419InspectorAgent& InspectorController::ensureInspectorAgent()
420{
421 if (!m_inspectorAgent) {
422 auto pageContext = pageAgentContext();
423 auto inspectorAgent = std::make_unique<InspectorAgent>(pageContext);
424 m_inspectorAgent = inspectorAgent.get();
425 m_instrumentingAgents->setInspectorAgent(m_inspectorAgent);
426 m_agents.append(WTFMove(inspectorAgent));
427 }
428 return *m_inspectorAgent;
429}
430
431InspectorDOMAgent& InspectorController::ensureDOMAgent()
432{
433 if (!m_inspectorDOMAgent) {
434 auto pageContext = pageAgentContext();
435 auto domAgent = std::make_unique<InspectorDOMAgent>(pageContext, m_overlay.get());
436 m_inspectorDOMAgent = domAgent.get();
437 m_agents.append(WTFMove(domAgent));
438 }
439 return *m_inspectorDOMAgent;
440}
441
442InspectorPageAgent& InspectorController::ensurePageAgent()
443{
444 if (!m_inspectorPageAgent) {
445 auto pageContext = pageAgentContext();
446 auto pageAgent = std::make_unique<InspectorPageAgent>(pageContext, m_inspectorClient, m_overlay.get());
447 m_inspectorPageAgent = pageAgent.get();
448 m_agents.append(WTFMove(pageAgent));
449 }
450 return *m_inspectorPageAgent;
451}
452
453bool InspectorController::developerExtrasEnabled() const
454{
455 return m_page.settings().developerExtrasEnabled();
456}
457
458bool InspectorController::canAccessInspectedScriptState(JSC::ExecState* scriptState) const
459{
460 JSLockHolder lock(scriptState);
461
462 JSDOMWindow* inspectedWindow = toJSDOMWindow(scriptState->vm(), scriptState->lexicalGlobalObject());
463 if (!inspectedWindow)
464 return false;
465
466 return BindingSecurity::shouldAllowAccessToDOMWindow(scriptState, inspectedWindow->wrapped(), DoNotReportSecurityError);
467}
468
469InspectorFunctionCallHandler InspectorController::functionCallHandler() const
470{
471 return WebCore::functionCallHandlerFromAnyThread;
472}
473
474InspectorEvaluateHandler InspectorController::evaluateHandler() const
475{
476 return WebCore::evaluateHandlerFromAnyThread;
477}
478
479void InspectorController::frontendInitialized()
480{
481 if (m_pauseAfterInitialization) {
482 m_pauseAfterInitialization = false;
483 if (PageDebuggerAgent* debuggerAgent = m_instrumentingAgents->pageDebuggerAgent()) {
484 ErrorString ignored;
485 debuggerAgent->pause(ignored);
486 }
487 }
488
489#if ENABLE(REMOTE_INSPECTOR)
490 if (m_isAutomaticInspection)
491 m_page.inspectorDebuggable().unpauseForInitializedInspector();
492#endif
493}
494
495Ref<Stopwatch> InspectorController::executionStopwatch()
496{
497 return m_executionStopwatch.copyRef();
498}
499
500PageScriptDebugServer& InspectorController::scriptDebugServer()
501{
502 return m_scriptDebugServer;
503}
504
505JSC::VM& InspectorController::vm()
506{
507 return commonVM();
508}
509
510void InspectorController::didComposite(Frame& frame)
511{
512 InspectorInstrumentation::didComposite(frame);
513}
514
515} // namespace WebCore
516