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:
PageRuntimeAgent(InjectedScriptManager * injectedScriptManager,Page * page)96 PageRuntimeAgent(InjectedScriptManager* injectedScriptManager, Page* page)
97 : InspectorRuntimeAgent(injectedScriptManager)
98 , m_inspectedPage(page) { }
~PageRuntimeAgent()99 virtual ~PageRuntimeAgent() { }
100
101 private:
getDefaultInspectedState()102 virtual ScriptState* getDefaultInspectedState() { return mainWorldScriptState(m_inspectedPage->mainFrame()); }
103 Page* m_inspectedPage;
104 };
105
106 }
107
InspectorAgent(Page * page,InspectorClient * client,InjectedScriptManager * injectedScriptManager)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(), m_state.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
~InspectorAgent()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
inspectedPageDestroyed()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
restoreInspectorStateFromCookie(const String & inspectorStateCookie)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(DATABASE)
200 m_databaseAgent->restore();
201 #endif
202
203 #if ENABLE(JAVASCRIPT_DEBUGGER)
204 m_debuggerAgent->restore();
205 m_profilerAgent->restore();
206 #endif
207 }
208
didClearWindowObjectInWorld(Frame * frame,DOMWrapperWorld * world)209 void InspectorAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
210 {
211 if (world != mainThreadNormalWorld())
212 return;
213
214 if (!m_inspectorExtensionAPI.isEmpty())
215 m_injectedScriptManager->injectScript(m_inspectorExtensionAPI, mainWorldScriptState(frame));
216 }
217
setFrontend(InspectorFrontend * inspectorFrontend)218 void InspectorAgent::setFrontend(InspectorFrontend* inspectorFrontend)
219 {
220 // We can reconnect to existing front-end -> unmute state.
221 m_state->unmute();
222
223 m_frontend = inspectorFrontend;
224
225 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
226 m_applicationCacheAgent->setFrontend(m_frontend);
227 #endif
228 m_pageAgent->setFrontend(m_frontend);
229 m_domAgent->setFrontend(m_frontend);
230 m_consoleAgent->setFrontend(m_frontend);
231 m_timelineAgent->setFrontend(m_frontend);
232 m_resourceAgent->setFrontend(m_frontend);
233 #if ENABLE(JAVASCRIPT_DEBUGGER)
234 m_debuggerAgent->setFrontend(m_frontend);
235 m_profilerAgent->setFrontend(m_frontend);
236 #endif
237 #if ENABLE(DATABASE)
238 m_databaseAgent->setFrontend(m_frontend);
239 #endif
240 #if ENABLE(DOM_STORAGE)
241 m_domStorageAgent->setFrontend(m_frontend);
242 #endif
243
244 if (!m_showPanelAfterVisible.isEmpty()) {
245 m_frontend->inspector()->showPanel(m_showPanelAfterVisible);
246 m_showPanelAfterVisible = String();
247 }
248 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(WORKERS)
249 WorkersMap::iterator workersEnd = m_workers.end();
250 for (WorkersMap::iterator it = m_workers.begin(); it != workersEnd; ++it) {
251 InspectorWorkerResource* worker = it->second.get();
252 m_frontend->inspector()->didCreateWorker(worker->id(), worker->url(), worker->isSharedWorker());
253 }
254 #endif
255 // Dispatch pending frontend commands
256 issueEvaluateForTestCommands();
257 }
258
disconnectFrontend()259 void InspectorAgent::disconnectFrontend()
260 {
261 if (!m_frontend)
262 return;
263
264 m_canIssueEvaluateForTestInFrontend = false;
265 m_pendingEvaluateTestCommands.clear();
266
267 // Destroying agents would change the state, but we don't want that.
268 // Pre-disconnect state will be used to restore inspector agents.
269 m_state->mute();
270
271 m_frontend = 0;
272
273 #if ENABLE(JAVASCRIPT_DEBUGGER)
274 m_debuggerAgent->clearFrontend();
275 m_browserDebuggerAgent->clearFrontend();
276 m_profilerAgent->clearFrontend();
277 #endif
278
279 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
280 m_applicationCacheAgent->clearFrontend();
281 #endif
282
283 m_consoleAgent->clearFrontend();
284 m_domAgent->clearFrontend();
285 m_timelineAgent->clearFrontend();
286 m_resourceAgent->clearFrontend();
287 #if ENABLE(DATABASE)
288 m_databaseAgent->clearFrontend();
289 #endif
290 #if ENABLE(DOM_STORAGE)
291 m_domStorageAgent->clearFrontend();
292 #endif
293 m_pageAgent->clearFrontend();
294 }
295
didCommitLoad()296 void InspectorAgent::didCommitLoad()
297 {
298 if (m_frontend)
299 m_frontend->inspector()->reset();
300
301 m_injectedScriptManager->discardInjectedScripts();
302 #if ENABLE(WORKERS)
303 m_workers.clear();
304 #endif
305 }
306
domContentLoadedEventFired()307 void InspectorAgent::domContentLoadedEventFired()
308 {
309 m_injectedScriptManager->injectedScriptHost()->clearInspectedNodes();
310 }
311
isMainResourceLoader(DocumentLoader * loader,const KURL & requestUrl)312 bool InspectorAgent::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
313 {
314 return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
315 }
316
317 #if ENABLE(WORKERS)
318 class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task {
319 public:
create(PassRefPtr<InspectorWorkerResource> worker,InspectorAgent::WorkerAction action)320 static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
321 {
322 return new PostWorkerNotificationToFrontendTask(worker, action);
323 }
324
325 private:
PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker,InspectorAgent::WorkerAction action)326 PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
327 : m_worker(worker)
328 , m_action(action)
329 {
330 }
331
performTask(ScriptExecutionContext * scriptContext)332 virtual void performTask(ScriptExecutionContext* scriptContext)
333 {
334 if (scriptContext->isDocument()) {
335 if (InspectorAgent* inspectorAgent = static_cast<Document*>(scriptContext)->page()->inspectorController()->m_inspectorAgent.get())
336 inspectorAgent->postWorkerNotificationToFrontend(*m_worker, m_action);
337 }
338 }
339
340 private:
341 RefPtr<InspectorWorkerResource> m_worker;
342 InspectorAgent::WorkerAction m_action;
343 };
344
postWorkerNotificationToFrontend(const InspectorWorkerResource & worker,InspectorAgent::WorkerAction action)345 void InspectorAgent::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorAgent::WorkerAction action)
346 {
347 if (!m_frontend)
348 return;
349 #if ENABLE(JAVASCRIPT_DEBUGGER)
350 switch (action) {
351 case InspectorAgent::WorkerCreated:
352 m_frontend->inspector()->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker());
353 break;
354 case InspectorAgent::WorkerDestroyed:
355 m_frontend->inspector()->didDestroyWorker(worker.id());
356 break;
357 }
358 #endif
359 }
360
didCreateWorker(intptr_t id,const String & url,bool isSharedWorker)361 void InspectorAgent::didCreateWorker(intptr_t id, const String& url, bool isSharedWorker)
362 {
363 if (!enabled())
364 return;
365
366 RefPtr<InspectorWorkerResource> workerResource(InspectorWorkerResource::create(id, url, isSharedWorker));
367 m_workers.set(id, workerResource);
368 if (m_inspectedPage && m_frontend)
369 m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource, InspectorAgent::WorkerCreated));
370 }
371
didDestroyWorker(intptr_t id)372 void InspectorAgent::didDestroyWorker(intptr_t id)
373 {
374 if (!enabled())
375 return;
376
377 WorkersMap::iterator workerResource = m_workers.find(id);
378 if (workerResource == m_workers.end())
379 return;
380 if (m_inspectedPage && m_frontend)
381 m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource->second, InspectorAgent::WorkerDestroyed));
382 m_workers.remove(workerResource);
383 }
384 #endif // ENABLE(WORKERS)
385
386 #if ENABLE(JAVASCRIPT_DEBUGGER)
showProfilesPanel()387 void InspectorAgent::showProfilesPanel()
388 {
389 showPanel(profilesPanelName);
390 }
391 #endif
392
evaluateForTestInFrontend(long callId,const String & script)393 void InspectorAgent::evaluateForTestInFrontend(long callId, const String& script)
394 {
395 m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
396 if (m_canIssueEvaluateForTestInFrontend)
397 issueEvaluateForTestCommands();
398 }
399
setInspectorExtensionAPI(const String & source)400 void InspectorAgent::setInspectorExtensionAPI(const String& source)
401 {
402 m_inspectorExtensionAPI = source;
403 }
404
inspectedURL() const405 KURL InspectorAgent::inspectedURL() const
406 {
407 return m_inspectedPage->mainFrame()->document()->url();
408 }
409
inspectedURLWithoutFragment() const410 KURL InspectorAgent::inspectedURLWithoutFragment() const
411 {
412 KURL url = inspectedURL();
413 url.removeFragmentIdentifier();
414 return url;
415 }
416
enabled() const417 bool InspectorAgent::enabled() const
418 {
419 if (!m_inspectedPage)
420 return false;
421 return m_inspectedPage->settings()->developerExtrasEnabled();
422 }
423
showConsole()424 void InspectorAgent::showConsole()
425 {
426 showPanel(consolePanelName);
427 }
428
showPanel(const String & panel)429 void InspectorAgent::showPanel(const String& panel)
430 {
431 if (!m_frontend) {
432 m_showPanelAfterVisible = panel;
433 return;
434 }
435 m_frontend->inspector()->showPanel(panel);
436 }
437
issueEvaluateForTestCommands()438 void InspectorAgent::issueEvaluateForTestCommands()
439 {
440 if (m_frontend) {
441 Vector<pair<long, String> > copy = m_pendingEvaluateTestCommands;
442 m_pendingEvaluateTestCommands.clear();
443 for (Vector<pair<long, String> >::iterator it = copy.begin(); m_frontend && it != copy.end(); ++it)
444 m_frontend->inspector()->evaluateForTestInFrontend((*it).first, (*it).second);
445 m_canIssueEvaluateForTestInFrontend = true;
446 }
447 }
448
449 } // namespace WebCore
450
451 #endif // ENABLE(INSPECTOR)
452