8a9d1d58ad2359d345ea748657209691fdefe79a
[WebKit-https.git] / Source / WebCore / inspector / InspectorAgent.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4  * Copyright (C) 2011 Google Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorAgent.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "Document.h"
37 #include "DocumentLoader.h"
38 #include "Frame.h"
39 #include "GraphicsContext.h"
40 #include "InjectedScriptHost.h"
41 #include "InjectedScriptManager.h"
42 #include "InspectorBrowserDebuggerAgent.h"
43 #include "InspectorCSSAgent.h"
44 #include "InspectorClient.h"
45 #include "InspectorConsoleAgent.h"
46 #include "InspectorController.h"
47 #include "InspectorDOMAgent.h"
48 #include "InspectorFrontend.h"
49 #include "InspectorInstrumentation.h"
50 #include "InspectorPageAgent.h"
51 #include "InspectorProfilerAgent.h"
52 #include "InspectorResourceAgent.h"
53 #include "InspectorRuntimeAgent.h"
54 #include "InspectorState.h"
55 #include "InspectorTimelineAgent.h"
56 #include "InspectorValues.h"
57 #include "InspectorWorkerResource.h"
58 #include "InstrumentingAgents.h"
59 #include "Page.h"
60 #include "PageDebuggerAgent.h"
61 #include "ResourceRequest.h"
62 #include "ScriptFunctionCall.h"
63 #include "ScriptObject.h"
64 #include "ScriptState.h"
65 #include "Settings.h"
66
67 #if ENABLE(DATABASE)
68 #include "InspectorDatabaseAgent.h"
69 #endif
70
71 #if ENABLE(DOM_STORAGE)
72 #include "InspectorDOMStorageAgent.h"
73 #endif
74
75 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
76 #include "InspectorApplicationCacheAgent.h"
77 #endif
78
79 using namespace std;
80
81 namespace WebCore {
82
83 namespace InspectorAgentState {
84 static const char timelineProfilerEnabled[] = "timelineProfilerEnabled";
85 static const char debuggerEnabled[] = "debuggerEnabled";
86 }
87
88 static const char scriptsPanelName[] = "scripts";
89 static const char consolePanelName[] = "console";
90 static const char profilesPanelName[] = "profiles";
91
92 namespace {
93
94 class PageRuntimeAgent : public InspectorRuntimeAgent {
95 public:
96     PageRuntimeAgent(InjectedScriptManager* injectedScriptManager, Page* page)
97         : InspectorRuntimeAgent(injectedScriptManager)
98         , m_inspectedPage(page) { }
99     virtual ~PageRuntimeAgent() { }
100
101 private:
102     virtual ScriptState* getDefaultInspectedState() { return mainWorldScriptState(m_inspectedPage->mainFrame()); }
103     Page* m_inspectedPage;
104 };
105
106 }
107
108 InspectorAgent::InspectorAgent(Page* page, InspectorClient* client, InjectedScriptManager* injectedScriptManager)
109     : m_inspectedPage(page)
110     , m_client(client)
111     , m_frontend(0)
112     , m_instrumentingAgents(new InstrumentingAgents())
113     , m_injectedScriptManager(injectedScriptManager)
114     , m_state(new InspectorState(client))
115     , m_pageAgent(InspectorPageAgent::create(m_instrumentingAgents.get(), page, injectedScriptManager))
116     , m_domAgent(InspectorDOMAgent::create(m_instrumentingAgents.get(), page, m_client, m_state.get(), injectedScriptManager))
117     , m_cssAgent(new InspectorCSSAgent(m_instrumentingAgents.get(), m_domAgent.get()))
118 #if ENABLE(DATABASE)
119     , m_databaseAgent(InspectorDatabaseAgent::create(m_instrumentingAgents.get()))
120 #endif
121 #if ENABLE(DOM_STORAGE)
122     , m_domStorageAgent(InspectorDOMStorageAgent::create(m_instrumentingAgents.get()))
123 #endif
124     , m_timelineAgent(InspectorTimelineAgent::create(m_instrumentingAgents.get(), m_state.get()))
125 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
126     , m_applicationCacheAgent(new InspectorApplicationCacheAgent(m_instrumentingAgents.get(), page))
127 #endif
128     , m_resourceAgent(InspectorResourceAgent::create(m_instrumentingAgents.get(), page, m_state.get()))
129     , m_runtimeAgent(adoptPtr(new PageRuntimeAgent(m_injectedScriptManager, page)))
130     , m_consoleAgent(new InspectorConsoleAgent(m_instrumentingAgents.get(), this, m_state.get(), injectedScriptManager, m_domAgent.get()))
131 #if ENABLE(JAVASCRIPT_DEBUGGER)
132     , m_debuggerAgent(PageDebuggerAgent::create(m_instrumentingAgents.get(), m_state.get(), page, injectedScriptManager))
133     , m_browserDebuggerAgent(InspectorBrowserDebuggerAgent::create(m_instrumentingAgents.get(), m_state.get(), m_domAgent.get(), m_debuggerAgent.get(), this))
134     , m_profilerAgent(InspectorProfilerAgent::create(m_instrumentingAgents.get(), m_consoleAgent.get(), page, m_state.get()))
135 #endif
136     , m_canIssueEvaluateForTestInFrontend(false)
137 {
138     ASSERT_ARG(page, page);
139     ASSERT_ARG(client, client);
140     InspectorInstrumentation::bindInspectorAgent(m_inspectedPage, this);
141     m_instrumentingAgents->setInspectorAgent(this);
142
143     m_injectedScriptManager->injectedScriptHost()->init(this
144         , m_consoleAgent.get()
145 #if ENABLE(DATABASE)
146         , m_databaseAgent.get()
147 #endif
148 #if ENABLE(DOM_STORAGE)
149         , m_domStorageAgent.get()
150 #endif
151 #if ENABLE(JAVASCRIPT_DEBUGGER)
152         , m_debuggerAgent.get()
153 #endif
154     );
155 }
156
157 InspectorAgent::~InspectorAgent()
158 {
159     m_instrumentingAgents->setInspectorAgent(0);
160
161     // These should have been cleared in inspectedPageDestroyed().
162     ASSERT(!m_client);
163     ASSERT(!m_inspectedPage);
164 }
165
166 void InspectorAgent::inspectedPageDestroyed()
167 {
168     if (m_frontend) {
169         m_frontend->inspector()->disconnectFromBackend();
170         disconnectFrontend();
171     }
172
173 #if ENABLE(JAVASCRIPT_DEBUGGER)
174     m_browserDebuggerAgent.clear();
175     m_debuggerAgent.clear();
176 #endif
177
178     ASSERT(m_inspectedPage);
179     InspectorInstrumentation::unbindInspectorAgent(m_inspectedPage);
180     m_inspectedPage = 0;
181
182     m_injectedScriptManager->disconnect();
183
184     m_client->inspectorDestroyed();
185     m_client = 0;
186 }
187
188 void InspectorAgent::restoreInspectorStateFromCookie(const String& inspectorStateCookie)
189 {
190     m_state->loadFromCookie(inspectorStateCookie);
191
192     m_frontend->inspector()->frontendReused();
193     m_pageAgent->restore();
194
195     m_domAgent->restore();
196     m_resourceAgent->restore();
197     m_timelineAgent->restore();
198
199 #if ENABLE(JAVASCRIPT_DEBUGGER)
200     m_debuggerAgent->restore();
201     m_profilerAgent->restore();
202 #endif
203 }
204
205 void InspectorAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
206 {
207     if (world != mainThreadNormalWorld())
208         return;
209
210     if (!m_inspectorExtensionAPI.isEmpty())
211         m_injectedScriptManager->injectScript(m_inspectorExtensionAPI, mainWorldScriptState(frame));
212 }
213
214 void InspectorAgent::setFrontend(InspectorFrontend* inspectorFrontend)
215 {
216     // We can reconnect to existing front-end -> unmute state.
217     m_state->unmute();
218
219     m_frontend = inspectorFrontend;
220
221 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
222     m_applicationCacheAgent->setFrontend(m_frontend);
223 #endif
224     m_pageAgent->setFrontend(m_frontend);
225     m_domAgent->setFrontend(m_frontend);
226     m_consoleAgent->setFrontend(m_frontend);
227     m_timelineAgent->setFrontend(m_frontend);
228     m_resourceAgent->setFrontend(m_frontend);
229 #if ENABLE(JAVASCRIPT_DEBUGGER)
230     m_debuggerAgent->setFrontend(m_frontend);
231     m_profilerAgent->setFrontend(m_frontend);
232 #endif
233 #if ENABLE(DATABASE)
234     m_databaseAgent->setFrontend(m_frontend);
235 #endif
236 #if ENABLE(DOM_STORAGE)
237     m_domStorageAgent->setFrontend(m_frontend);
238 #endif
239
240     if (!m_showPanelAfterVisible.isEmpty()) {
241         m_frontend->inspector()->showPanel(m_showPanelAfterVisible);
242         m_showPanelAfterVisible = String();
243     }
244 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(WORKERS)
245     WorkersMap::iterator workersEnd = m_workers.end();
246     for (WorkersMap::iterator it = m_workers.begin(); it != workersEnd; ++it) {
247         InspectorWorkerResource* worker = it->second.get();
248         m_frontend->inspector()->didCreateWorker(worker->id(), worker->url(), worker->isSharedWorker());
249     }
250 #endif
251     // Dispatch pending frontend commands
252     issueEvaluateForTestCommands();
253 }
254
255 void InspectorAgent::disconnectFrontend()
256 {
257     if (!m_frontend)
258         return;
259
260     m_canIssueEvaluateForTestInFrontend = false;
261     m_pendingEvaluateTestCommands.clear();
262
263     // Destroying agents would change the state, but we don't want that.
264     // Pre-disconnect state will be used to restore inspector agents.
265     m_state->mute();
266
267     m_frontend = 0;
268
269 #if ENABLE(JAVASCRIPT_DEBUGGER)
270     m_debuggerAgent->clearFrontend();
271     m_browserDebuggerAgent->clearFrontend();
272     m_profilerAgent->clearFrontend();
273 #endif
274
275 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
276     m_applicationCacheAgent->clearFrontend();
277 #endif
278
279     m_consoleAgent->clearFrontend();
280     m_domAgent->clearFrontend();
281     m_timelineAgent->clearFrontend();
282     m_resourceAgent->clearFrontend();
283 #if ENABLE(DATABASE)
284     m_databaseAgent->clearFrontend();
285 #endif
286 #if ENABLE(DOM_STORAGE)
287     m_domStorageAgent->clearFrontend();
288 #endif
289     m_pageAgent->clearFrontend();
290 }
291
292 void InspectorAgent::didCommitLoad()
293 {
294     if (m_frontend)
295         m_frontend->inspector()->reset();
296
297     m_injectedScriptManager->discardInjectedScripts();
298 #if ENABLE(WORKERS)
299     m_workers.clear();
300 #endif
301 }
302
303 void InspectorAgent::domContentLoadedEventFired()
304 {
305     m_injectedScriptManager->injectedScriptHost()->clearInspectedNodes();
306 }
307
308 bool InspectorAgent::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
309 {
310     return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
311 }
312
313 #if ENABLE(WORKERS)
314 class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task {
315 public:
316     static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
317     {
318         return new PostWorkerNotificationToFrontendTask(worker, action);
319     }
320
321 private:
322     PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
323         : m_worker(worker)
324         , m_action(action)
325     {
326     }
327
328     virtual void performTask(ScriptExecutionContext* scriptContext)
329     {
330         if (scriptContext->isDocument()) {
331             if (InspectorAgent* inspectorAgent = static_cast<Document*>(scriptContext)->page()->inspectorController()->m_inspectorAgent.get())
332                 inspectorAgent->postWorkerNotificationToFrontend(*m_worker, m_action);
333         }
334     }
335
336 private:
337     RefPtr<InspectorWorkerResource> m_worker;
338     InspectorAgent::WorkerAction m_action;
339 };
340
341 void InspectorAgent::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorAgent::WorkerAction action)
342 {
343     if (!m_frontend)
344         return;
345 #if ENABLE(JAVASCRIPT_DEBUGGER)
346     switch (action) {
347     case InspectorAgent::WorkerCreated:
348         m_frontend->inspector()->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker());
349         break;
350     case InspectorAgent::WorkerDestroyed:
351         m_frontend->inspector()->didDestroyWorker(worker.id());
352         break;
353     }
354 #endif
355 }
356
357 void InspectorAgent::didCreateWorker(intptr_t id, const String& url, bool isSharedWorker)
358 {
359     if (!enabled())
360         return;
361
362     RefPtr<InspectorWorkerResource> workerResource(InspectorWorkerResource::create(id, url, isSharedWorker));
363     m_workers.set(id, workerResource);
364     if (m_inspectedPage && m_frontend)
365         m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource, InspectorAgent::WorkerCreated));
366 }
367
368 void InspectorAgent::didDestroyWorker(intptr_t id)
369 {
370     if (!enabled())
371         return;
372
373     WorkersMap::iterator workerResource = m_workers.find(id);
374     if (workerResource == m_workers.end())
375         return;
376     if (m_inspectedPage && m_frontend)
377         m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource->second, InspectorAgent::WorkerDestroyed));
378     m_workers.remove(workerResource);
379 }
380 #endif // ENABLE(WORKERS)
381
382 #if ENABLE(JAVASCRIPT_DEBUGGER)
383 void InspectorAgent::showProfilesPanel()
384 {
385     showPanel(profilesPanelName);
386 }
387 #endif
388
389 void InspectorAgent::evaluateForTestInFrontend(long callId, const String& script)
390 {
391     m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
392     if (m_canIssueEvaluateForTestInFrontend)
393         issueEvaluateForTestCommands();
394 }
395
396 void InspectorAgent::didEvaluateForTestInFrontend(ErrorString*, long callId, const String& jsonResult)
397 {
398     ScriptState* scriptState = scriptStateFromPage(debuggerWorld(), m_inspectedPage);
399     ScriptObject window;
400     ScriptGlobalObject::get(scriptState, "window", window);
401     ScriptFunctionCall function(window, "didEvaluateForTestInFrontend");
402     function.appendArgument(callId);
403     function.appendArgument(jsonResult);
404     function.call();
405 }
406
407 void InspectorAgent::setInspectorExtensionAPI(const String& source)
408 {
409     m_inspectorExtensionAPI = source;
410 }
411
412 KURL InspectorAgent::inspectedURL() const
413 {
414     return m_inspectedPage->mainFrame()->document()->url();
415 }
416
417 KURL InspectorAgent::inspectedURLWithoutFragment() const
418 {
419     KURL url = inspectedURL();
420     url.removeFragmentIdentifier();
421     return url;
422 }
423
424 bool InspectorAgent::enabled() const
425 {
426     if (!m_inspectedPage)
427         return false;
428     return m_inspectedPage->settings()->developerExtrasEnabled();
429 }
430
431 void InspectorAgent::showConsole()
432 {
433     showPanel(consolePanelName);
434 }
435
436 void InspectorAgent::showPanel(const String& panel)
437 {
438     if (!m_frontend) {
439         m_showPanelAfterVisible = panel;
440         return;
441     }
442     m_frontend->inspector()->showPanel(panel);
443 }
444
445 void InspectorAgent::issueEvaluateForTestCommands()
446 {
447     if (m_frontend) {
448         Vector<pair<long, String> > copy = m_pendingEvaluateTestCommands;
449         m_pendingEvaluateTestCommands.clear();
450         for (Vector<pair<long, String> >::iterator it = copy.begin(); m_frontend && it != copy.end(); ++it)
451             m_frontend->inspector()->evaluateForTestInFrontend((*it).first, (*it).second);
452         m_canIssueEvaluateForTestInFrontend = true;
453     }
454 }
455
456 } // namespace WebCore
457
458 #endif // ENABLE(INSPECTOR)