1 /* 2 * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Matt Lilek <webkit (at) 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(), 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 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(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 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 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 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 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 307 void InspectorAgent::domContentLoadedEventFired() 308 { 309 m_injectedScriptManager->injectedScriptHost()->clearInspectedNodes(); 310 } 311 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: 320 static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action) 321 { 322 return new PostWorkerNotificationToFrontendTask(worker, action); 323 } 324 325 private: 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 340 private: 341 RefPtr<InspectorWorkerResource> m_worker; 342 InspectorAgent::WorkerAction m_action; 343 }; 344 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 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 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) 387 void InspectorAgent::showProfilesPanel() 388 { 389 showPanel(profilesPanelName); 390 } 391 #endif 392 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 400 void InspectorAgent::setInspectorExtensionAPI(const String& source) 401 { 402 m_inspectorExtensionAPI = source; 403 } 404 405 KURL InspectorAgent::inspectedURL() const 406 { 407 return m_inspectedPage->mainFrame()->document()->url(); 408 } 409 410 KURL InspectorAgent::inspectedURLWithoutFragment() const 411 { 412 KURL url = inspectedURL(); 413 url.removeFragmentIdentifier(); 414 return url; 415 } 416 417 bool InspectorAgent::enabled() const 418 { 419 if (!m_inspectedPage) 420 return false; 421 return m_inspectedPage->settings()->developerExtrasEnabled(); 422 } 423 424 void InspectorAgent::showConsole() 425 { 426 showPanel(consolePanelName); 427 } 428 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 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