Web Inspector: DOM: lazily create the agent
[WebKit-https.git] / Source / WebCore / inspector / InspectorController.cpp
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 "InspectorFrontendClient.h"
51 #include "InspectorIndexedDBAgent.h"
52 #include "InspectorInstrumentation.h"
53 #include "InspectorLayerTreeAgent.h"
54 #include "InspectorMemoryAgent.h"
55 #include "InspectorPageAgent.h"
56 #include "InspectorTimelineAgent.h"
57 #include "InspectorWorkerAgent.h"
58 #include "InstrumentingAgents.h"
59 #include "JSDOMBindingSecurity.h"
60 #include "JSDOMWindow.h"
61 #include "JSDOMWindowCustom.h"
62 #include "JSExecState.h"
63 #include "Page.h"
64 #include "PageAuditAgent.h"
65 #include "PageConsoleAgent.h"
66 #include "PageDebuggerAgent.h"
67 #include "PageHeapAgent.h"
68 #include "PageNetworkAgent.h"
69 #include "PageRuntimeAgent.h"
70 #include "PageScriptDebugServer.h"
71 #include "Settings.h"
72 #include "WebInjectedScriptHost.h"
73 #include "WebInjectedScriptManager.h"
74 #include <JavaScriptCore/IdentifiersFactory.h>
75 #include <JavaScriptCore/InspectorAgent.h>
76 #include <JavaScriptCore/InspectorBackendDispatcher.h>
77 #include <JavaScriptCore/InspectorBackendDispatchers.h>
78 #include <JavaScriptCore/InspectorFrontendDispatchers.h>
79 #include <JavaScriptCore/InspectorFrontendRouter.h>
80 #include <JavaScriptCore/InspectorScriptProfilerAgent.h>
81 #include <JavaScriptCore/JSLock.h>
82 #include <wtf/Stopwatch.h>
83
84 #if ENABLE(REMOTE_INSPECTOR)
85 #include "PageDebuggable.h"
86 #endif
87
88 namespace WebCore {
89
90 using namespace JSC;
91 using namespace Inspector;
92
93 InspectorController::InspectorController(Page& page, InspectorClient* inspectorClient)
94     : m_instrumentingAgents(InstrumentingAgents::create(*this))
95     , m_injectedScriptManager(std::make_unique<WebInjectedScriptManager>(*this, WebInjectedScriptHost::create()))
96     , m_frontendRouter(FrontendRouter::create())
97     , m_backendDispatcher(BackendDispatcher::create(m_frontendRouter.copyRef()))
98     , m_overlay(std::make_unique<InspectorOverlay>(page, inspectorClient))
99     , m_executionStopwatch(Stopwatch::create())
100     , m_scriptDebugServer(page)
101     , m_page(page)
102     , m_inspectorClient(inspectorClient)
103 {
104     ASSERT_ARG(inspectorClient, inspectorClient);
105
106     auto pageContext = pageAgentContext();
107
108     auto inspectorAgentPtr = std::make_unique<InspectorAgent>(pageContext);
109     m_inspectorAgent = inspectorAgentPtr.get();
110     m_instrumentingAgents->setInspectorAgent(m_inspectorAgent);
111     m_agents.append(WTFMove(inspectorAgentPtr));
112
113     auto pageAgentPtr = std::make_unique<InspectorPageAgent>(pageContext, inspectorClient, m_overlay.get());
114     m_pageAgent = pageAgentPtr.get();
115     m_agents.append(WTFMove(pageAgentPtr));
116
117     auto consoleAgent = std::make_unique<PageConsoleAgent>(pageContext);
118     m_instrumentingAgents->setWebConsoleAgent(consoleAgent.get());
119     m_agents.append(WTFMove(consoleAgent));
120
121     ASSERT(m_injectedScriptManager->commandLineAPIHost());
122     if (auto* commandLineAPIHost = m_injectedScriptManager->commandLineAPIHost())
123         commandLineAPIHost->init(m_instrumentingAgents.copyRef());
124 }
125
126 InspectorController::~InspectorController()
127 {
128     m_instrumentingAgents->reset();
129     ASSERT(!m_inspectorClient);
130 }
131
132 PageAgentContext InspectorController::pageAgentContext()
133 {
134     AgentContext baseContext = {
135         *this,
136         *m_injectedScriptManager,
137         m_frontendRouter.get(),
138         m_backendDispatcher.get()
139     };
140
141     WebAgentContext webContext = {
142         baseContext,
143         m_instrumentingAgents.get()
144     };
145
146     PageAgentContext pageContext = {
147         webContext,
148         m_page
149     };
150
151     return pageContext;
152 }
153
154 void InspectorController::createLazyAgents()
155 {
156     if (m_didCreateLazyAgents)
157         return;
158
159     m_didCreateLazyAgents = true;
160
161     auto pageContext = pageAgentContext();
162
163     m_agents.append(std::make_unique<PageRuntimeAgent>(pageContext));
164
165     auto debuggerAgent = std::make_unique<PageDebuggerAgent>(pageContext, m_pageAgent);
166     auto debuggerAgentPtr = debuggerAgent.get();
167     m_agents.append(WTFMove(debuggerAgent));
168
169     m_agents.append(std::make_unique<PageNetworkAgent>(pageContext, m_pageAgent));
170     m_agents.append(std::make_unique<InspectorCSSAgent>(pageContext));
171     m_agents.append(std::make_unique<InspectorDOMAgent>(pageContext, m_overlay.get()));
172     m_agents.append(std::make_unique<InspectorDOMDebuggerAgent>(pageContext, debuggerAgentPtr));
173     m_agents.append(std::make_unique<InspectorApplicationCacheAgent>(pageContext, m_pageAgent));
174     m_agents.append(std::make_unique<InspectorLayerTreeAgent>(pageContext));
175     m_agents.append(std::make_unique<InspectorWorkerAgent>(pageContext));
176     m_agents.append(std::make_unique<InspectorDOMStorageAgent>(pageContext));
177     m_agents.append(std::make_unique<InspectorDatabaseAgent>(pageContext));
178 #if ENABLE(INDEXED_DATABASE)
179     m_agents.append(std::make_unique<InspectorIndexedDBAgent>(pageContext, m_pageAgent));
180 #endif
181
182     auto scriptProfilerAgentPtr = std::make_unique<InspectorScriptProfilerAgent>(pageContext);
183     m_instrumentingAgents->setInspectorScriptProfilerAgent(scriptProfilerAgentPtr.get());
184     m_agents.append(WTFMove(scriptProfilerAgentPtr));
185
186 #if ENABLE(RESOURCE_USAGE)
187     m_agents.append(std::make_unique<InspectorCPUProfilerAgent>(pageContext));
188     m_agents.append(std::make_unique<InspectorMemoryAgent>(pageContext));
189 #endif
190     m_agents.append(std::make_unique<PageHeapAgent>(pageContext));
191     m_agents.append(std::make_unique<PageAuditAgent>(pageContext));
192     m_agents.append(std::make_unique<InspectorCanvasAgent>(pageContext));
193     m_agents.append(std::make_unique<InspectorTimelineAgent>(pageContext));
194 }
195
196 void InspectorController::inspectedPageDestroyed()
197 {
198     // Clean up resources and disconnect local and remote frontends.
199     disconnectAllFrontends();
200
201     // Disconnect the client.
202     m_inspectorClient->inspectedPageDestroyed();
203     m_inspectorClient = nullptr;
204
205     m_agents.discardValues();
206 }
207
208 void InspectorController::setInspectorFrontendClient(InspectorFrontendClient* inspectorFrontendClient)
209 {
210     m_inspectorFrontendClient = inspectorFrontendClient;
211 }
212
213 bool InspectorController::hasLocalFrontend() const
214 {
215     return m_frontendRouter->hasLocalFrontend();
216 }
217
218 bool InspectorController::hasRemoteFrontend() const
219 {
220     return m_frontendRouter->hasRemoteFrontend();
221 }
222
223 unsigned InspectorController::inspectionLevel() const
224 {
225     return m_inspectorFrontendClient ? m_inspectorFrontendClient->inspectionLevel() : 0;
226 }
227
228 void InspectorController::didClearWindowObjectInWorld(Frame& frame, DOMWrapperWorld& world)
229 {
230     if (&world != &mainThreadNormalWorld())
231         return;
232
233     if (frame.isMainFrame())
234         m_injectedScriptManager->discardInjectedScripts();
235
236     // If the page is supposed to serve as InspectorFrontend notify inspector frontend
237     // client that it's cleared so that the client can expose inspector bindings.
238     if (m_inspectorFrontendClient && frame.isMainFrame())
239         m_inspectorFrontendClient->windowObjectCleared();
240 }
241
242 void InspectorController::connectFrontend(Inspector::FrontendChannel& frontendChannel, bool isAutomaticInspection, bool immediatelyPause)
243 {
244     ASSERT(m_inspectorClient);
245
246     // If a frontend has connected enable the developer extras and keep them enabled.
247     m_page.settings().setDeveloperExtrasEnabled(true);
248
249     createLazyAgents();
250
251     bool connectedFirstFrontend = !m_frontendRouter->hasFrontends();
252     m_isAutomaticInspection = isAutomaticInspection;
253     m_pauseAfterInitialization = immediatelyPause;
254
255     m_frontendRouter->connectFrontend(frontendChannel);
256
257     InspectorInstrumentation::frontendCreated();
258
259     if (connectedFirstFrontend) {
260         InspectorInstrumentation::registerInstrumentingAgents(m_instrumentingAgents.get());
261         m_agents.didCreateFrontendAndBackend(&m_frontendRouter.get(), &m_backendDispatcher.get());
262     }
263
264     m_inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount());
265
266 #if ENABLE(REMOTE_INSPECTOR)
267     if (hasLocalFrontend())
268         m_page.remoteInspectorInformationDidChange();
269 #endif
270 }
271
272 void InspectorController::disconnectFrontend(FrontendChannel& frontendChannel)
273 {
274     m_frontendRouter->disconnectFrontend(frontendChannel);
275
276     m_isAutomaticInspection = false;
277     m_pauseAfterInitialization = false;
278
279     InspectorInstrumentation::frontendDeleted();
280
281     bool disconnectedLastFrontend = !m_frontendRouter->hasFrontends();
282     if (disconnectedLastFrontend) {
283         // Notify agents first.
284         m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectorDestroyed);
285
286         // Clean up inspector resources.
287         m_injectedScriptManager->discardInjectedScripts();
288
289         // Unplug all instrumentations since they aren't needed now.
290         InspectorInstrumentation::unregisterInstrumentingAgents(m_instrumentingAgents.get());
291     }
292
293     m_inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount());
294
295 #if ENABLE(REMOTE_INSPECTOR)
296     if (disconnectedLastFrontend)
297         m_page.remoteInspectorInformationDidChange();
298 #endif
299 }
300
301 void InspectorController::disconnectAllFrontends()
302 {
303     // If the local frontend page was destroyed, close the window.
304     if (m_inspectorFrontendClient)
305         m_inspectorFrontendClient->closeWindow();
306
307     // The frontend should call setInspectorFrontendClient(nullptr) under closeWindow().
308     ASSERT(!m_inspectorFrontendClient);
309
310     if (!m_frontendRouter->hasFrontends())
311         return;
312
313     for (unsigned i = 0; i < m_frontendRouter->frontendCount(); ++i)
314         InspectorInstrumentation::frontendDeleted();
315
316     // Unplug all instrumentations to prevent further agent callbacks.
317     InspectorInstrumentation::unregisterInstrumentingAgents(m_instrumentingAgents.get());
318
319     // Notify agents first, since they may need to use InspectorClient.
320     m_agents.willDestroyFrontendAndBackend(DisconnectReason::InspectedTargetDestroyed);
321
322     // Clean up inspector resources.
323     m_injectedScriptManager->disconnect();
324
325     // Disconnect any remaining remote frontends.
326     m_frontendRouter->disconnectAllFrontends();
327     m_isAutomaticInspection = false;
328     m_pauseAfterInitialization = false;
329
330     m_inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount());
331
332 #if ENABLE(REMOTE_INSPECTOR)
333     m_page.remoteInspectorInformationDidChange();
334 #endif
335 }
336
337 void InspectorController::show()
338 {
339     ASSERT(!hasRemoteFrontend());
340
341     if (!enabled())
342         return;
343
344     if (m_frontendRouter->hasLocalFrontend())
345         m_inspectorClient->bringFrontendToFront();
346     else if (Inspector::FrontendChannel* frontendChannel = m_inspectorClient->openLocalFrontend(this))
347         connectFrontend(*frontendChannel);
348 }
349
350 void InspectorController::setIsUnderTest(bool value)
351 {
352     if (value == m_isUnderTest)
353         return;
354
355     m_isUnderTest = value;
356
357     // <rdar://problem/26768628> Try to catch suspicious scenarios where we may have a dangling frontend while running tests.
358     RELEASE_ASSERT(!m_isUnderTest || !m_frontendRouter->hasFrontends());
359 }
360
361 void InspectorController::evaluateForTestInFrontend(const String& script)
362 {
363     m_inspectorAgent->evaluateForTestInFrontend(script);
364 }
365
366 void InspectorController::drawHighlight(GraphicsContext& context) const
367 {
368     m_overlay->paint(context);
369 }
370
371 void InspectorController::getHighlight(Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem) const
372 {
373     m_overlay->getHighlight(highlight, coordinateSystem);
374 }
375
376 void InspectorController::inspect(Node* node)
377 {
378     if (!enabled())
379         return;
380
381     if (!hasRemoteFrontend())
382         show();
383
384     if (auto* domAgent = m_instrumentingAgents->inspectorDOMAgent())
385         domAgent->inspect(node);
386 }
387
388 bool InspectorController::enabled() const
389 {
390     return developerExtrasEnabled();
391 }
392
393 Page& InspectorController::inspectedPage() const
394 {
395     return m_page;
396 }
397
398 void InspectorController::dispatchMessageFromFrontend(const String& message)
399 {
400     m_backendDispatcher->dispatch(message);
401 }
402
403 void InspectorController::hideHighlight()
404 {
405     if (auto* domAgent = m_instrumentingAgents->inspectorDOMAgent()) {
406         ErrorString unused;
407         domAgent->hideHighlight(unused);
408     }
409 }
410
411 Node* InspectorController::highlightedNode() const
412 {
413     return m_overlay->highlightedNode();
414 }
415
416 void InspectorController::setIndicating(bool indicating)
417 {
418 #if !PLATFORM(IOS_FAMILY)
419     m_overlay->setIndicating(indicating);
420 #else
421     if (indicating)
422         m_inspectorClient->showInspectorIndication();
423     else
424         m_inspectorClient->hideInspectorIndication();
425 #endif
426 }
427
428 bool InspectorController::developerExtrasEnabled() const
429 {
430     return m_page.settings().developerExtrasEnabled();
431 }
432
433 bool InspectorController::canAccessInspectedScriptState(JSC::ExecState* scriptState) const
434 {
435     JSLockHolder lock(scriptState);
436
437     JSDOMWindow* inspectedWindow = toJSDOMWindow(scriptState->vm(), scriptState->lexicalGlobalObject());
438     if (!inspectedWindow)
439         return false;
440
441     return BindingSecurity::shouldAllowAccessToDOMWindow(scriptState, inspectedWindow->wrapped(), DoNotReportSecurityError);
442 }
443
444 InspectorFunctionCallHandler InspectorController::functionCallHandler() const
445 {
446     return WebCore::functionCallHandlerFromAnyThread;
447 }
448
449 InspectorEvaluateHandler InspectorController::evaluateHandler() const
450 {
451     return WebCore::evaluateHandlerFromAnyThread;
452 }
453
454 void InspectorController::frontendInitialized()
455 {
456     if (m_pauseAfterInitialization) {
457         m_pauseAfterInitialization = false;
458         if (PageDebuggerAgent* debuggerAgent = m_instrumentingAgents->pageDebuggerAgent()) {
459             ErrorString ignored;
460             debuggerAgent->pause(ignored);
461         }
462     }
463
464 #if ENABLE(REMOTE_INSPECTOR)
465     if (m_isAutomaticInspection)
466         m_page.inspectorDebuggable().unpauseForInitializedInspector();
467 #endif
468 }
469
470 Ref<Stopwatch> InspectorController::executionStopwatch()
471 {
472     return m_executionStopwatch.copyRef();
473 }
474
475 PageScriptDebugServer& InspectorController::scriptDebugServer()
476 {
477     return m_scriptDebugServer;
478 }
479
480 JSC::VM& InspectorController::vm()
481 {
482     return commonVM();
483 }
484
485 void InspectorController::didComposite(Frame& frame)
486 {
487     InspectorInstrumentation::didComposite(frame);
488 }
489
490 } // namespace WebCore