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