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 79using namespace std; 80 81namespace WebCore { 82 83namespace InspectorAgentState { 84static const char timelineProfilerEnabled[] = "timelineProfilerEnabled"; 85static const char debuggerEnabled[] = "debuggerEnabled"; 86} 87 88static const char scriptsPanelName[] = "scripts"; 89static const char consolePanelName[] = "console"; 90static const char profilesPanelName[] = "profiles"; 91 92namespace { 93 94class PageRuntimeAgent : public InspectorRuntimeAgent { 95public: 96 PageRuntimeAgent(InjectedScriptManager* injectedScriptManager, Page* page) 97 : InspectorRuntimeAgent(injectedScriptManager) 98 , m_inspectedPage(page) { } 99 virtual ~PageRuntimeAgent() { } 100 101private: 102 virtual ScriptState* getDefaultInspectedState() { return mainWorldScriptState(m_inspectedPage->mainFrame()); } 103 Page* m_inspectedPage; 104}; 105 106} 107 108InspectorAgent::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 157InspectorAgent::~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 166void 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 188void 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 209void 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 218void 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 259void 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 296void 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 307void InspectorAgent::domContentLoadedEventFired() 308{ 309 m_injectedScriptManager->injectedScriptHost()->clearInspectedNodes(); 310} 311 312bool InspectorAgent::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl) 313{ 314 return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL(); 315} 316 317#if ENABLE(WORKERS) 318class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task { 319public: 320 static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action) 321 { 322 return new PostWorkerNotificationToFrontendTask(worker, action); 323 } 324 325private: 326 PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action) 327 : m_worker(worker) 328 , m_action(action) 329 { 330 } 331 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 340private: 341 RefPtr<InspectorWorkerResource> m_worker; 342 InspectorAgent::WorkerAction m_action; 343}; 344 345void 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 361void 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 372void 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) 387void InspectorAgent::showProfilesPanel() 388{ 389 showPanel(profilesPanelName); 390} 391#endif 392 393void 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 400void InspectorAgent::setInspectorExtensionAPI(const String& source) 401{ 402 m_inspectorExtensionAPI = source; 403} 404 405KURL InspectorAgent::inspectedURL() const 406{ 407 return m_inspectedPage->mainFrame()->document()->url(); 408} 409 410KURL InspectorAgent::inspectedURLWithoutFragment() const 411{ 412 KURL url = inspectedURL(); 413 url.removeFragmentIdentifier(); 414 return url; 415} 416 417bool InspectorAgent::enabled() const 418{ 419 if (!m_inspectedPage) 420 return false; 421 return m_inspectedPage->settings()->developerExtrasEnabled(); 422} 423 424void InspectorAgent::showConsole() 425{ 426 showPanel(consolePanelName); 427} 428 429void 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 438void 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