blob: 0cfc5d28a4204d0d06cf2e2513a49b7d0fb120ea [file] [log] [blame]
/*
* Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "InspectorAgent.h"
#if ENABLE(INSPECTOR)
#include "Document.h"
#include "DocumentLoader.h"
#include "Frame.h"
#include "GraphicsContext.h"
#include "InjectedScriptHost.h"
#include "InjectedScriptManager.h"
#include "InspectorBrowserDebuggerAgent.h"
#include "InspectorCSSAgent.h"
#include "InspectorClient.h"
#include "InspectorConsoleAgent.h"
#include "InspectorController.h"
#include "InspectorDOMAgent.h"
#include "InspectorFrontend.h"
#include "InspectorInstrumentation.h"
#include "InspectorPageAgent.h"
#include "InspectorProfilerAgent.h"
#include "InspectorResourceAgent.h"
#include "InspectorRuntimeAgent.h"
#include "InspectorState.h"
#include "InspectorTimelineAgent.h"
#include "InspectorValues.h"
#include "InspectorWorkerResource.h"
#include "InstrumentingAgents.h"
#include "Page.h"
#include "PageDebuggerAgent.h"
#include "ResourceRequest.h"
#include "ScriptFunctionCall.h"
#include "ScriptObject.h"
#include "ScriptState.h"
#include "Settings.h"
#if ENABLE(DATABASE)
#include "InspectorDatabaseAgent.h"
#endif
#if ENABLE(DOM_STORAGE)
#include "InspectorDOMStorageAgent.h"
#endif
#if ENABLE(OFFLINE_WEB_APPLICATIONS)
#include "InspectorApplicationCacheAgent.h"
#endif
using namespace std;
namespace WebCore {
namespace InspectorAgentState {
static const char timelineProfilerEnabled[] = "timelineProfilerEnabled";
static const char debuggerEnabled[] = "debuggerEnabled";
}
static const char scriptsPanelName[] = "scripts";
static const char consolePanelName[] = "console";
static const char profilesPanelName[] = "profiles";
namespace {
class PageRuntimeAgent : public InspectorRuntimeAgent {
public:
PageRuntimeAgent(InjectedScriptManager* injectedScriptManager, Page* page)
: InspectorRuntimeAgent(injectedScriptManager)
, m_inspectedPage(page) { }
virtual ~PageRuntimeAgent() { }
private:
virtual ScriptState* getDefaultInspectedState() { return mainWorldScriptState(m_inspectedPage->mainFrame()); }
Page* m_inspectedPage;
};
}
InspectorAgent::InspectorAgent(Page* page, InspectorClient* client, InjectedScriptManager* injectedScriptManager)
: m_inspectedPage(page)
, m_client(client)
, m_frontend(0)
, m_instrumentingAgents(new InstrumentingAgents())
, m_injectedScriptManager(injectedScriptManager)
, m_state(new InspectorState(client))
, m_pageAgent(InspectorPageAgent::create(m_instrumentingAgents.get(), page, injectedScriptManager))
, m_domAgent(InspectorDOMAgent::create(m_instrumentingAgents.get(), page, m_client, m_state.get(), injectedScriptManager))
, m_cssAgent(new InspectorCSSAgent(m_instrumentingAgents.get(), m_domAgent.get()))
#if ENABLE(DATABASE)
, m_databaseAgent(InspectorDatabaseAgent::create(m_instrumentingAgents.get(), m_state.get()))
#endif
#if ENABLE(DOM_STORAGE)
, m_domStorageAgent(InspectorDOMStorageAgent::create(m_instrumentingAgents.get()))
#endif
, m_timelineAgent(InspectorTimelineAgent::create(m_instrumentingAgents.get(), m_state.get()))
#if ENABLE(OFFLINE_WEB_APPLICATIONS)
, m_applicationCacheAgent(new InspectorApplicationCacheAgent(m_instrumentingAgents.get(), page))
#endif
, m_resourceAgent(InspectorResourceAgent::create(m_instrumentingAgents.get(), page, m_state.get()))
, m_runtimeAgent(adoptPtr(new PageRuntimeAgent(m_injectedScriptManager, page)))
, m_consoleAgent(new InspectorConsoleAgent(m_instrumentingAgents.get(), this, m_state.get(), injectedScriptManager, m_domAgent.get()))
#if ENABLE(JAVASCRIPT_DEBUGGER)
, m_debuggerAgent(PageDebuggerAgent::create(m_instrumentingAgents.get(), m_state.get(), page, injectedScriptManager))
, m_browserDebuggerAgent(InspectorBrowserDebuggerAgent::create(m_instrumentingAgents.get(), m_state.get(), m_domAgent.get(), m_debuggerAgent.get(), this))
, m_profilerAgent(InspectorProfilerAgent::create(m_instrumentingAgents.get(), m_consoleAgent.get(), page, m_state.get()))
#endif
, m_canIssueEvaluateForTestInFrontend(false)
{
ASSERT_ARG(page, page);
ASSERT_ARG(client, client);
InspectorInstrumentation::bindInspectorAgent(m_inspectedPage, this);
m_instrumentingAgents->setInspectorAgent(this);
m_injectedScriptManager->injectedScriptHost()->init(this
, m_consoleAgent.get()
#if ENABLE(DATABASE)
, m_databaseAgent.get()
#endif
#if ENABLE(DOM_STORAGE)
, m_domStorageAgent.get()
#endif
#if ENABLE(JAVASCRIPT_DEBUGGER)
, m_debuggerAgent.get()
#endif
);
}
InspectorAgent::~InspectorAgent()
{
m_instrumentingAgents->setInspectorAgent(0);
// These should have been cleared in inspectedPageDestroyed().
ASSERT(!m_client);
ASSERT(!m_inspectedPage);
}
void InspectorAgent::inspectedPageDestroyed()
{
if (m_frontend) {
m_frontend->inspector()->disconnectFromBackend();
disconnectFrontend();
}
#if ENABLE(JAVASCRIPT_DEBUGGER)
m_browserDebuggerAgent.clear();
m_debuggerAgent.clear();
#endif
ASSERT(m_inspectedPage);
InspectorInstrumentation::unbindInspectorAgent(m_inspectedPage);
m_inspectedPage = 0;
m_injectedScriptManager->disconnect();
m_client->inspectorDestroyed();
m_client = 0;
}
void InspectorAgent::restoreInspectorStateFromCookie(const String& inspectorStateCookie)
{
m_state->loadFromCookie(inspectorStateCookie);
m_frontend->inspector()->frontendReused();
m_pageAgent->restore();
m_domAgent->restore();
m_resourceAgent->restore();
m_timelineAgent->restore();
#if ENABLE(DATABASE)
m_databaseAgent->restore();
#endif
#if ENABLE(JAVASCRIPT_DEBUGGER)
m_debuggerAgent->restore();
m_profilerAgent->restore();
#endif
}
void InspectorAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
{
if (world != mainThreadNormalWorld())
return;
if (!m_inspectorExtensionAPI.isEmpty())
m_injectedScriptManager->injectScript(m_inspectorExtensionAPI, mainWorldScriptState(frame));
}
void InspectorAgent::setFrontend(InspectorFrontend* inspectorFrontend)
{
// We can reconnect to existing front-end -> unmute state.
m_state->unmute();
m_frontend = inspectorFrontend;
#if ENABLE(OFFLINE_WEB_APPLICATIONS)
m_applicationCacheAgent->setFrontend(m_frontend);
#endif
m_pageAgent->setFrontend(m_frontend);
m_domAgent->setFrontend(m_frontend);
m_consoleAgent->setFrontend(m_frontend);
m_timelineAgent->setFrontend(m_frontend);
m_resourceAgent->setFrontend(m_frontend);
#if ENABLE(JAVASCRIPT_DEBUGGER)
m_debuggerAgent->setFrontend(m_frontend);
m_profilerAgent->setFrontend(m_frontend);
#endif
#if ENABLE(DATABASE)
m_databaseAgent->setFrontend(m_frontend);
#endif
#if ENABLE(DOM_STORAGE)
m_domStorageAgent->setFrontend(m_frontend);
#endif
if (!m_showPanelAfterVisible.isEmpty()) {
m_frontend->inspector()->showPanel(m_showPanelAfterVisible);
m_showPanelAfterVisible = String();
}
#if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(WORKERS)
WorkersMap::iterator workersEnd = m_workers.end();
for (WorkersMap::iterator it = m_workers.begin(); it != workersEnd; ++it) {
InspectorWorkerResource* worker = it->second.get();
m_frontend->inspector()->didCreateWorker(worker->id(), worker->url(), worker->isSharedWorker());
}
#endif
// Dispatch pending frontend commands
issueEvaluateForTestCommands();
}
void InspectorAgent::disconnectFrontend()
{
if (!m_frontend)
return;
m_canIssueEvaluateForTestInFrontend = false;
m_pendingEvaluateTestCommands.clear();
// Destroying agents would change the state, but we don't want that.
// Pre-disconnect state will be used to restore inspector agents.
m_state->mute();
m_frontend = 0;
#if ENABLE(JAVASCRIPT_DEBUGGER)
m_debuggerAgent->clearFrontend();
m_browserDebuggerAgent->clearFrontend();
m_profilerAgent->clearFrontend();
#endif
#if ENABLE(OFFLINE_WEB_APPLICATIONS)
m_applicationCacheAgent->clearFrontend();
#endif
m_consoleAgent->clearFrontend();
m_domAgent->clearFrontend();
m_timelineAgent->clearFrontend();
m_resourceAgent->clearFrontend();
#if ENABLE(DATABASE)
m_databaseAgent->clearFrontend();
#endif
#if ENABLE(DOM_STORAGE)
m_domStorageAgent->clearFrontend();
#endif
m_pageAgent->clearFrontend();
}
void InspectorAgent::didCommitLoad()
{
if (m_frontend)
m_frontend->inspector()->reset();
m_injectedScriptManager->discardInjectedScripts();
#if ENABLE(WORKERS)
m_workers.clear();
#endif
}
void InspectorAgent::domContentLoadedEventFired()
{
m_injectedScriptManager->injectedScriptHost()->clearInspectedNodes();
}
bool InspectorAgent::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
{
return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
}
#if ENABLE(WORKERS)
class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task {
public:
static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
{
return new PostWorkerNotificationToFrontendTask(worker, action);
}
private:
PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
: m_worker(worker)
, m_action(action)
{
}
virtual void performTask(ScriptExecutionContext* scriptContext)
{
if (scriptContext->isDocument()) {
if (InspectorAgent* inspectorAgent = static_cast<Document*>(scriptContext)->page()->inspectorController()->m_inspectorAgent.get())
inspectorAgent->postWorkerNotificationToFrontend(*m_worker, m_action);
}
}
private:
RefPtr<InspectorWorkerResource> m_worker;
InspectorAgent::WorkerAction m_action;
};
void InspectorAgent::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorAgent::WorkerAction action)
{
if (!m_frontend)
return;
#if ENABLE(JAVASCRIPT_DEBUGGER)
switch (action) {
case InspectorAgent::WorkerCreated:
m_frontend->inspector()->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker());
break;
case InspectorAgent::WorkerDestroyed:
m_frontend->inspector()->didDestroyWorker(worker.id());
break;
}
#endif
}
void InspectorAgent::didCreateWorker(intptr_t id, const String& url, bool isSharedWorker)
{
if (!enabled())
return;
RefPtr<InspectorWorkerResource> workerResource(InspectorWorkerResource::create(id, url, isSharedWorker));
m_workers.set(id, workerResource);
if (m_inspectedPage && m_frontend)
m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource, InspectorAgent::WorkerCreated));
}
void InspectorAgent::didDestroyWorker(intptr_t id)
{
if (!enabled())
return;
WorkersMap::iterator workerResource = m_workers.find(id);
if (workerResource == m_workers.end())
return;
if (m_inspectedPage && m_frontend)
m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource->second, InspectorAgent::WorkerDestroyed));
m_workers.remove(workerResource);
}
#endif // ENABLE(WORKERS)
#if ENABLE(JAVASCRIPT_DEBUGGER)
void InspectorAgent::showProfilesPanel()
{
showPanel(profilesPanelName);
}
#endif
void InspectorAgent::evaluateForTestInFrontend(long callId, const String& script)
{
m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
if (m_canIssueEvaluateForTestInFrontend)
issueEvaluateForTestCommands();
}
void InspectorAgent::setInspectorExtensionAPI(const String& source)
{
m_inspectorExtensionAPI = source;
}
KURL InspectorAgent::inspectedURL() const
{
return m_inspectedPage->mainFrame()->document()->url();
}
KURL InspectorAgent::inspectedURLWithoutFragment() const
{
KURL url = inspectedURL();
url.removeFragmentIdentifier();
return url;
}
bool InspectorAgent::enabled() const
{
if (!m_inspectedPage)
return false;
return m_inspectedPage->settings()->developerExtrasEnabled();
}
void InspectorAgent::showConsole()
{
showPanel(consolePanelName);
}
void InspectorAgent::showPanel(const String& panel)
{
if (!m_frontend) {
m_showPanelAfterVisible = panel;
return;
}
m_frontend->inspector()->showPanel(panel);
}
void InspectorAgent::issueEvaluateForTestCommands()
{
if (m_frontend) {
Vector<pair<long, String> > copy = m_pendingEvaluateTestCommands;
m_pendingEvaluateTestCommands.clear();
for (Vector<pair<long, String> >::iterator it = copy.begin(); m_frontend && it != copy.end(); ++it)
m_frontend->inspector()->evaluateForTestInFrontend((*it).first, (*it).second);
m_canIssueEvaluateForTestInFrontend = true;
}
}
} // namespace WebCore
#endif // ENABLE(INSPECTOR)