Home | History | Annotate | Download | only in inspector
      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