summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/inspector/InspectorController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/inspector/InspectorController.cpp')
-rw-r--r--Source/WebCore/inspector/InspectorController.cpp1835
1 files changed, 1835 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/InspectorController.cpp b/Source/WebCore/inspector/InspectorController.cpp
new file mode 100644
index 0000000..7248dc9
--- /dev/null
+++ b/Source/WebCore/inspector/InspectorController.cpp
@@ -0,0 +1,1835 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
+ *
+ * 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 "InspectorController.h"
+
+#if ENABLE(INSPECTOR)
+
+#include "CachedResource.h"
+#include "CachedResourceLoader.h"
+#include "Chrome.h"
+#include "Console.h"
+#include "ConsoleMessage.h"
+#include "Cookie.h"
+#include "CookieJar.h"
+#include "DOMWindow.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "Element.h"
+#include "FloatConversion.h"
+#include "FloatQuad.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoadRequest.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTTPHeaderMap.h"
+#include "HitTestResult.h"
+#include "InjectedScript.h"
+#include "InjectedScriptHost.h"
+#include "InspectorBackendDispatcher.h"
+#include "InspectorCSSAgent.h"
+#include "InspectorClient.h"
+#include "InspectorDOMAgent.h"
+#include "InspectorDOMStorageResource.h"
+#include "InspectorDatabaseResource.h"
+#include "InspectorDebuggerAgent.h"
+#include "InspectorFrontend.h"
+#include "InspectorFrontendClient.h"
+#include "InspectorInstrumentation.h"
+#include "InspectorProfilerAgent.h"
+#include "InspectorResourceAgent.h"
+#include "InspectorState.h"
+#include "InspectorTimelineAgent.h"
+#include "InspectorValues.h"
+#include "InspectorWorkerResource.h"
+#include "IntRect.h"
+#include "Page.h"
+#include "ProgressTracker.h"
+#include "Range.h"
+#include "RenderInline.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "ScriptArguments.h"
+#include "ScriptCallStack.h"
+#include "ScriptFunctionCall.h"
+#include "ScriptObject.h"
+#include "ScriptProfile.h"
+#include "ScriptProfiler.h"
+#include "ScriptSourceCode.h"
+#include "ScriptState.h"
+#include "SecurityOrigin.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include "TextEncoding.h"
+#include "TextIterator.h"
+#include "UserGestureIndicator.h"
+#include "WindowFeatures.h"
+#include <wtf/text/StringConcatenate.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/RefCounted.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/UnusedParam.h>
+
+#if ENABLE(DATABASE)
+#include "Database.h"
+#include "InspectorDatabaseAgent.h"
+#endif
+
+#if ENABLE(DOM_STORAGE)
+#include "InspectorDOMStorageAgent.h"
+#include "Storage.h"
+#include "StorageArea.h"
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+#include "InspectorApplicationCacheAgent.h"
+#endif
+
+#if ENABLE(FILE_SYSTEM)
+#include "InspectorFileSystemAgent.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+const char* const InspectorController::ElementsPanel = "elements";
+const char* const InspectorController::ConsolePanel = "console";
+const char* const InspectorController::ScriptsPanel = "scripts";
+const char* const InspectorController::ProfilesPanel = "profiles";
+
+const unsigned InspectorController::defaultAttachedHeight = 300;
+
+static const unsigned maximumConsoleMessages = 1000;
+static const unsigned expireConsoleMessagesStep = 100;
+
+InspectorController::InspectorController(Page* page, InspectorClient* client)
+ : m_inspectedPage(page)
+ , m_client(client)
+ , m_openingFrontend(false)
+ , m_cssAgent(new InspectorCSSAgent())
+ , m_mainResourceIdentifier(0)
+ , m_expiredConsoleMessageCount(0)
+ , m_previousMessage(0)
+ , m_settingsLoaded(false)
+ , m_inspectorBackendDispatcher(new InspectorBackendDispatcher(this))
+ , m_injectedScriptHost(InjectedScriptHost::create(this))
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ , m_attachDebuggerWhenShown(false)
+ , m_hasXHRBreakpointWithEmptyURL(false)
+ , m_profilerAgent(InspectorProfilerAgent::create(this))
+#endif
+{
+ m_state = new InspectorState(client);
+ ASSERT_ARG(page, page);
+ ASSERT_ARG(client, client);
+}
+
+InspectorController::~InspectorController()
+{
+ // These should have been cleared in inspectedPageDestroyed().
+ ASSERT(!m_client);
+ ASSERT(!m_inspectedPage);
+ ASSERT(!m_highlightedNode);
+
+ releaseFrontendLifetimeAgents();
+
+ m_injectedScriptHost->disconnectController();
+}
+
+void InspectorController::inspectedPageDestroyed()
+{
+ if (m_frontend)
+ m_frontend->disconnectFromBackend();
+
+ hideHighlight();
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ m_debuggerAgent.clear();
+#endif
+ ASSERT(m_inspectedPage);
+ m_inspectedPage = 0;
+
+ m_client->inspectorDestroyed();
+ m_client = 0;
+}
+
+bool InspectorController::enabled() const
+{
+ if (!m_inspectedPage)
+ return false;
+ return m_inspectedPage->settings()->developerExtrasEnabled();
+}
+
+bool InspectorController::inspectorStartsAttached()
+{
+ return m_state->getBoolean(InspectorState::inspectorStartsAttached);
+}
+
+void InspectorController::setInspectorStartsAttached(bool attached)
+{
+ m_state->setBoolean(InspectorState::inspectorStartsAttached, attached);
+}
+
+void InspectorController::setInspectorAttachedHeight(long height)
+{
+ m_state->setLong(InspectorState::inspectorAttachedHeight, height);
+}
+
+int InspectorController::inspectorAttachedHeight() const
+{
+ return m_state->getBoolean(InspectorState::inspectorAttachedHeight);
+}
+
+bool InspectorController::searchingForNodeInPage() const
+{
+ return m_state->getBoolean(InspectorState::searchingForNode);
+}
+
+void InspectorController::getInspectorState(RefPtr<InspectorObject>* state)
+{
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ if (m_debuggerAgent)
+ m_state->setLong(InspectorState::pauseOnExceptionsState, m_debuggerAgent->pauseOnExceptionsState());
+#endif
+ *state = m_state->generateStateObjectForFrontend();
+}
+
+void InspectorController::restoreInspectorStateFromCookie(const String& inspectorStateCookie)
+{
+ m_state->restoreFromInspectorCookie(inspectorStateCookie);
+ if (m_state->getBoolean(InspectorState::timelineProfilerEnabled))
+ startTimelineProfiler();
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ if (m_state->getBoolean(InspectorState::userInitiatedProfiling))
+ startUserInitiatedProfiling();
+#endif
+}
+
+void InspectorController::inspect(Node* node)
+{
+ if (!enabled())
+ return;
+
+ show();
+
+ if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
+ node = node->parentNode();
+ m_nodeToFocus = node;
+
+ if (!m_frontend)
+ return;
+
+ focusNode();
+}
+
+void InspectorController::focusNode()
+{
+ if (!enabled())
+ return;
+
+ ASSERT(m_frontend);
+ ASSERT(m_nodeToFocus);
+
+ long id = m_domAgent->pushNodePathToFrontend(m_nodeToFocus.get());
+ m_frontend->updateFocusedNode(id);
+ m_nodeToFocus = 0;
+}
+
+void InspectorController::highlight(Node* node)
+{
+ if (!enabled())
+ return;
+ ASSERT_ARG(node, node);
+ m_highlightedNode = node;
+ m_client->highlight(node);
+}
+
+void InspectorController::highlightDOMNode(long nodeId)
+{
+ Node* node = 0;
+ if (m_domAgent && (node = m_domAgent->nodeForId(nodeId)))
+ highlight(node);
+}
+
+void InspectorController::highlightFrame(unsigned long frameId)
+{
+ Frame* mainFrame = m_inspectedPage->mainFrame();
+ for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) {
+ if (reinterpret_cast<uintptr_t>(frame) == frameId && frame->ownerElement()) {
+ highlight(frame->ownerElement());
+ return;
+ }
+ }
+}
+
+void InspectorController::hideHighlight()
+{
+ if (!enabled())
+ return;
+ m_highlightedNode = 0;
+ m_client->hideHighlight();
+}
+
+void InspectorController::setConsoleMessagesEnabled(bool enabled, bool* newState)
+{
+ *newState = enabled;
+ setConsoleMessagesEnabled(enabled);
+}
+
+void InspectorController::setConsoleMessagesEnabled(bool enabled)
+{
+ m_state->setBoolean(InspectorState::consoleMessagesEnabled, enabled);
+ if (!enabled)
+ return;
+
+ if (m_expiredConsoleMessageCount)
+ m_frontend->updateConsoleMessageExpiredCount(m_expiredConsoleMessageCount);
+ unsigned messageCount = m_consoleMessages.size();
+ for (unsigned i = 0; i < messageCount; ++i)
+ m_consoleMessages[i]->addToFrontend(m_frontend.get(), m_injectedScriptHost.get());
+}
+
+void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
+{
+ if (!enabled())
+ return;
+
+ addConsoleMessage(new ConsoleMessage(source, type, level, message, arguments, callStack));
+}
+
+void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
+{
+ if (!enabled())
+ return;
+
+ addConsoleMessage(new ConsoleMessage(source, type, level, message, lineNumber, sourceID));
+}
+
+void InspectorController::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage)
+{
+ ASSERT(enabled());
+ ASSERT_ARG(consoleMessage, consoleMessage);
+
+ if (m_previousMessage && m_previousMessage->isEqual(consoleMessage.get())) {
+ m_previousMessage->incrementCount();
+ if (m_state->getBoolean(InspectorState::consoleMessagesEnabled) && m_frontend)
+ m_previousMessage->updateRepeatCountInConsole(m_frontend.get());
+ } else {
+ m_previousMessage = consoleMessage.get();
+ m_consoleMessages.append(consoleMessage);
+ if (m_state->getBoolean(InspectorState::consoleMessagesEnabled) && m_frontend)
+ m_previousMessage->addToFrontend(m_frontend.get(), m_injectedScriptHost.get());
+ }
+
+ if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) {
+ m_expiredConsoleMessageCount += expireConsoleMessagesStep;
+ m_consoleMessages.remove(0, expireConsoleMessagesStep);
+ }
+}
+
+void InspectorController::clearConsoleMessages()
+{
+ m_consoleMessages.clear();
+ m_expiredConsoleMessageCount = 0;
+ m_previousMessage = 0;
+ m_injectedScriptHost->releaseWrapperObjectGroup(0 /* release the group in all scripts */, "console");
+ if (m_domAgent)
+ m_domAgent->releaseDanglingNodes();
+ if (m_frontend)
+ m_frontend->consoleMessagesCleared();
+}
+
+void InspectorController::startGroup(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, bool collapsed)
+{
+ addConsoleMessage(new ConsoleMessage(JSMessageSource, collapsed ? StartGroupCollapsedMessageType : StartGroupMessageType, LogMessageLevel, "", arguments, callStack));
+}
+
+void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL)
+{
+ addConsoleMessage(new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL));
+}
+
+void InspectorController::markTimeline(const String& message)
+{
+ if (timelineAgent())
+ timelineAgent()->didMarkTimeline(message);
+}
+
+void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
+{
+ if (!enabled() || !searchingForNodeInPage())
+ return;
+
+ Node* node = result.innerNode();
+ while (node && node->nodeType() == Node::TEXT_NODE)
+ node = node->parentNode();
+ if (node)
+ highlight(node);
+}
+
+void InspectorController::handleMousePress()
+{
+ if (!enabled())
+ return;
+
+ ASSERT(searchingForNodeInPage());
+ if (!m_highlightedNode)
+ return;
+
+ RefPtr<Node> node = m_highlightedNode;
+ setSearchingForNode(false);
+ inspect(node.get());
+}
+
+void InspectorController::setInspectorFrontendClient(PassOwnPtr<InspectorFrontendClient> client)
+{
+ ASSERT(!m_inspectorFrontendClient);
+ m_inspectorFrontendClient = client;
+}
+
+void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
+{
+ // If the page is supposed to serve as InspectorFrontend notify inspetor frontend
+ // client that it's cleared so that the client can expose inspector bindings.
+ if (m_inspectorFrontendClient && frame == m_inspectedPage->mainFrame())
+ m_inspectorFrontendClient->windowObjectCleared();
+
+ if (enabled()) {
+ if (m_frontend && frame == m_inspectedPage->mainFrame())
+ m_injectedScriptHost->discardInjectedScripts();
+ if (m_scriptsToEvaluateOnLoad.size()) {
+ ScriptState* scriptState = mainWorldScriptState(frame);
+ for (Vector<String>::iterator it = m_scriptsToEvaluateOnLoad.begin();
+ it != m_scriptsToEvaluateOnLoad.end(); ++it) {
+ m_injectedScriptHost->injectScript(*it, scriptState);
+ }
+ }
+ }
+ if (!m_inspectorExtensionAPI.isEmpty())
+ m_injectedScriptHost->injectScript(m_inspectorExtensionAPI, mainWorldScriptState(frame));
+}
+
+void InspectorController::setSearchingForNode(bool enabled)
+{
+ if (searchingForNodeInPage() == enabled)
+ return;
+ m_state->setBoolean(InspectorState::searchingForNode, enabled);
+ if (!enabled)
+ hideHighlight();
+}
+
+void InspectorController::setSearchingForNode(bool enabled, bool* newState)
+{
+ *newState = enabled;
+ setSearchingForNode(enabled);
+}
+
+void InspectorController::setMonitoringXHREnabled(bool enabled, bool* newState)
+{
+ *newState = enabled;
+ m_state->setBoolean(InspectorState::monitoringXHR, enabled);
+}
+
+void InspectorController::connectFrontend()
+{
+ m_openingFrontend = false;
+ releaseFrontendLifetimeAgents();
+ m_frontend = new InspectorFrontend(m_client);
+ m_domAgent = InspectorDOMAgent::create(m_frontend.get());
+ m_resourceAgent = InspectorResourceAgent::create(m_inspectedPage, m_frontend.get());
+
+ m_cssAgent->setDOMAgent(m_domAgent.get());
+
+#if ENABLE(DATABASE)
+ m_databaseAgent = InspectorDatabaseAgent::create(&m_databaseResources, m_frontend.get());
+#endif
+
+#if ENABLE(DOM_STORAGE)
+ m_domStorageAgent = InspectorDOMStorageAgent::create(&m_domStorageResources, m_frontend.get());
+#endif
+
+ if (m_timelineAgent)
+ m_timelineAgent->resetFrontendProxyObject(m_frontend.get());
+
+ // Initialize Web Inspector title.
+ m_frontend->inspectedURLChanged(m_inspectedPage->mainFrame()->loader()->url().string());
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ m_applicationCacheAgent = new InspectorApplicationCacheAgent(this, m_frontend.get());
+#endif
+
+#if ENABLE(FILE_SYSTEM)
+ m_fileSystemAgent = InspectorFileSystemAgent::create(this, m_frontend.get());
+#endif
+
+ if (!InspectorInstrumentation::hasFrontends())
+ ScriptController::setCaptureCallStackForUncaughtExceptions(true);
+ InspectorInstrumentation::frontendCreated();
+}
+
+void InspectorController::reuseFrontend()
+{
+ connectFrontend();
+ restoreDebugger();
+ restoreProfiler(ProfilerRestoreResetAgent);
+}
+
+void InspectorController::show()
+{
+ if (!enabled())
+ return;
+
+ if (m_openingFrontend)
+ return;
+
+ if (m_frontend)
+ m_frontend->bringToFront();
+ else {
+ m_openingFrontend = true;
+ m_client->openInspectorFrontend(this);
+ }
+}
+
+void InspectorController::showPanel(const String& panel)
+{
+ if (!enabled())
+ return;
+
+ show();
+
+ if (!m_frontend) {
+ m_showAfterVisible = panel;
+ return;
+ }
+ m_frontend->showPanel(panel);
+}
+
+void InspectorController::close()
+{
+ if (!m_frontend)
+ return;
+ m_frontend->disconnectFromBackend();
+ disconnectFrontend();
+}
+
+void InspectorController::disconnectFrontend()
+{
+ if (!m_frontend)
+ return;
+
+ m_frontend.clear();
+
+ InspectorInstrumentation::frontendDeleted();
+ if (!InspectorInstrumentation::hasFrontends())
+ ScriptController::setCaptureCallStackForUncaughtExceptions(false);
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ // If the window is being closed with the debugger enabled,
+ // remember this state to re-enable debugger on the next window
+ // opening.
+ bool debuggerWasEnabled = debuggerEnabled();
+ disableDebugger();
+ m_attachDebuggerWhenShown = debuggerWasEnabled;
+#endif
+ setSearchingForNode(false);
+ unbindAllResources();
+ stopTimelineProfiler();
+
+ hideHighlight();
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ m_profilerAgent->setFrontend(0);
+ m_profilerAgent->stopUserInitiatedProfiling(true);
+#endif
+
+ releaseFrontendLifetimeAgents();
+ m_timelineAgent.clear();
+ m_extraHeaders.clear();
+}
+
+void InspectorController::releaseFrontendLifetimeAgents()
+{
+ m_resourceAgent.clear();
+
+ // This should be invoked prior to m_domAgent destruction.
+ m_cssAgent->setDOMAgent(0);
+
+ // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are
+ // no references to the DOM agent from the DOM tree.
+ if (m_domAgent)
+ m_domAgent->reset();
+ m_domAgent.clear();
+
+#if ENABLE(DATABASE)
+ if (m_databaseAgent)
+ m_databaseAgent->clearFrontend();
+ m_databaseAgent.clear();
+#endif
+
+#if ENABLE(DOM_STORAGE)
+ m_domStorageAgent.clear();
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ m_applicationCacheAgent.clear();
+#endif
+
+#if ENABLE(FILE_SYSTEM)
+ if (m_fileSystemAgent)
+ m_fileSystemAgent->stop();
+ m_fileSystemAgent.clear();
+#endif
+}
+
+void InspectorController::populateScriptObjects()
+{
+ ASSERT(m_frontend);
+ if (!m_frontend)
+ return;
+
+ if (!m_showAfterVisible.isEmpty()) {
+ showPanel(m_showAfterVisible);
+ m_showAfterVisible = "";
+ }
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ if (m_profilerAgent->enabled())
+ m_frontend->profilerWasEnabled();
+#endif
+
+ m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
+
+ if (m_nodeToFocus)
+ focusNode();
+
+#if ENABLE(DATABASE)
+ DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
+ for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
+ it->second->bind(m_frontend.get());
+#endif
+#if ENABLE(DOM_STORAGE)
+ DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
+ for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
+ it->second->bind(m_frontend.get());
+#endif
+#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->didCreateWorker(worker->id(), worker->url(), worker->isSharedWorker());
+ }
+#endif
+
+ // Dispatch pending frontend commands
+ for (Vector<pair<long, String> >::iterator it = m_pendingEvaluateTestCommands.begin(); it != m_pendingEvaluateTestCommands.end(); ++it)
+ m_frontend->evaluateForTestInFrontend((*it).first, (*it).second);
+ m_pendingEvaluateTestCommands.clear();
+
+ restoreDebugger();
+ restoreProfiler(ProfilerRestoreNoAction);
+}
+
+void InspectorController::restoreDebugger()
+{
+ ASSERT(m_frontend);
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ if (InspectorDebuggerAgent::isDebuggerAlwaysEnabled())
+ enableDebuggerFromFrontend(false);
+ else {
+ if (m_state->getBoolean(InspectorState::debuggerAlwaysEnabled) || m_attachDebuggerWhenShown)
+ enableDebugger();
+ }
+#endif
+}
+
+void InspectorController::restoreProfiler(ProfilerRestoreAction action)
+{
+ ASSERT(m_frontend);
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ m_profilerAgent->setFrontend(m_frontend.get());
+ if (!ScriptProfiler::isProfilerAlwaysEnabled() && m_state->getBoolean(InspectorState::profilerAlwaysEnabled))
+ enableProfiler();
+ if (action == ProfilerRestoreResetAgent)
+ m_profilerAgent->resetState();
+#endif
+}
+
+void InspectorController::unbindAllResources()
+{
+#if ENABLE(DATABASE)
+ DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
+ for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
+ it->second->unbind();
+#endif
+#if ENABLE(DOM_STORAGE)
+ DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
+ for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
+ it->second->unbind();
+#endif
+ if (m_timelineAgent)
+ m_timelineAgent->reset();
+}
+
+void InspectorController::didCommitLoad(DocumentLoader* loader)
+{
+ if (!enabled())
+ return;
+
+ if (m_resourceAgent)
+ m_resourceAgent->didCommitLoad(loader);
+
+ ASSERT(m_inspectedPage);
+
+ if (loader->frame() == m_inspectedPage->mainFrame()) {
+ if (m_frontend)
+ m_frontend->inspectedURLChanged(loader->url().string());
+
+ m_injectedScriptHost->discardInjectedScripts();
+ clearConsoleMessages();
+
+ m_times.clear();
+ m_counts.clear();
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ if (m_debuggerAgent) {
+ m_debuggerAgent->clearForPageNavigation();
+ restoreStickyBreakpoints();
+ }
+#endif
+
+#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC)
+ m_profilerAgent->stopUserInitiatedProfiling(true);
+ m_profilerAgent->resetState();
+#endif
+
+ // unbindAllResources should be called before database and DOM storage
+ // resources are cleared so that it has a chance to unbind them.
+ unbindAllResources();
+
+ if (m_frontend) {
+ m_frontend->reset();
+ m_domAgent->reset();
+ m_cssAgent->reset();
+ }
+#if ENABLE(WORKERS)
+ m_workers.clear();
+#endif
+#if ENABLE(DATABASE)
+ m_databaseResources.clear();
+#endif
+#if ENABLE(DOM_STORAGE)
+ m_domStorageResources.clear();
+#endif
+
+ if (m_frontend) {
+ m_mainResourceIdentifier = 0;
+ m_frontend->didCommitLoad();
+ m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
+ }
+ }
+}
+
+void InspectorController::frameDetachedFromParent(Frame* rootFrame)
+{
+ if (!enabled())
+ return;
+
+ if (m_resourceAgent)
+ m_resourceAgent->frameDetachedFromParent(rootFrame);
+}
+
+void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* cachedResource)
+{
+ if (!enabled())
+ return;
+
+ ensureSettingsLoaded();
+
+ if (m_resourceAgent)
+ m_resourceAgent->didLoadResourceFromMemoryCache(loader, cachedResource);
+}
+
+void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
+{
+ if (!enabled())
+ return;
+ ASSERT(m_inspectedPage);
+
+ bool isMainResource = isMainResourceLoader(loader, request.url());
+ if (isMainResource)
+ m_mainResourceIdentifier = identifier;
+
+ ensureSettingsLoaded();
+
+ if (m_resourceAgent)
+ m_resourceAgent->identifierForInitialRequest(identifier, request.url(), loader);
+}
+
+void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loader, const KURL& url)
+{
+ if (!enabled() || !isMainResourceLoader(loader, url))
+ return;
+
+ if (m_timelineAgent)
+ m_timelineAgent->didMarkDOMContentEvent();
+ if (m_frontend)
+ m_frontend->domContentEventFired(currentTime());
+}
+
+void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url)
+{
+ if (!enabled() || !isMainResourceLoader(loader, url))
+ return;
+
+ if (m_timelineAgent)
+ m_timelineAgent->didMarkLoadEvent();
+ if (m_frontend)
+ m_frontend->loadEventFired(currentTime());
+}
+
+bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
+{
+ return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
+}
+
+void InspectorController::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
+{
+ if (!enabled())
+ return;
+
+ request.setReportLoadTiming(true);
+
+ if (m_frontend) {
+ // Only enable raw headers if front-end is attached, as otherwise we may lack
+ // permissions to fetch the headers.
+ request.setReportRawHeaders(true);
+
+ if (m_extraHeaders) {
+ HTTPHeaderMap::const_iterator end = m_extraHeaders->end();
+ for (HTTPHeaderMap::const_iterator it = m_extraHeaders->begin(); it != end; ++it)
+ request.setHTTPHeaderField(it->first, it->second);
+ }
+ }
+
+ bool isMainResource = m_mainResourceIdentifier == identifier;
+ if (m_timelineAgent)
+ m_timelineAgent->willSendResourceRequest(identifier, isMainResource, request);
+
+ if (m_resourceAgent)
+ m_resourceAgent->willSendRequest(identifier, request, redirectResponse);
+}
+
+void InspectorController::markResourceAsCached(unsigned long identifier)
+{
+ if (!enabled())
+ return;
+
+ if (m_resourceAgent)
+ m_resourceAgent->markResourceAsCached(identifier);
+}
+
+void InspectorController::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response)
+{
+ if (!enabled())
+ return;
+
+ if (m_resourceAgent)
+ m_resourceAgent->didReceiveResponse(identifier, loader, response);
+
+ if (response.httpStatusCode() >= 400) {
+ String message = makeString("Failed to load resource: the server responded with a status of ", String::number(response.httpStatusCode()), " (", response.httpStatusText(), ')');
+ addConsoleMessage(new ConsoleMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, response.url().string(), identifier));
+ }
+}
+
+void InspectorController::didReceiveContentLength(unsigned long identifier, int lengthReceived)
+{
+ if (!enabled())
+ return;
+
+ if (m_resourceAgent)
+ m_resourceAgent->didReceiveContentLength(identifier, lengthReceived);
+}
+
+void InspectorController::didFinishLoading(unsigned long identifier, double finishTime)
+{
+ if (!enabled())
+ return;
+
+ if (m_timelineAgent)
+ m_timelineAgent->didFinishLoadingResource(identifier, false, finishTime);
+
+ if (m_resourceAgent)
+ m_resourceAgent->didFinishLoading(identifier, finishTime);
+}
+
+void InspectorController::didFailLoading(unsigned long identifier, const ResourceError& error)
+{
+ if (!enabled())
+ return;
+
+ if (m_timelineAgent)
+ m_timelineAgent->didFinishLoadingResource(identifier, true, 0);
+
+ String message = "Failed to load resource";
+ if (!error.localizedDescription().isEmpty())
+ message += ": " + error.localizedDescription();
+ addConsoleMessage(new ConsoleMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, error.failingURL(), identifier));
+
+ if (m_resourceAgent)
+ m_resourceAgent->didFailLoading(identifier, error);
+}
+
+void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const String& sourceString, const String& url, const String& sendURL, unsigned sendLineNumber)
+{
+ if (!enabled())
+ return;
+
+ if (m_state->getBoolean(InspectorState::monitoringXHR))
+ addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, "XHR finished loading: \"" + url + "\".", sendLineNumber, sendURL);
+
+ if (m_resourceAgent)
+ m_resourceAgent->setInitialContent(identifier, sourceString, "XHR");
+}
+
+void InspectorController::scriptImported(unsigned long identifier, const String& sourceString)
+{
+ if (!enabled())
+ return;
+
+ if (m_resourceAgent)
+ m_resourceAgent->setInitialContent(identifier, sourceString, "Script");
+}
+
+void InspectorController::ensureSettingsLoaded()
+{
+ if (m_settingsLoaded)
+ return;
+ m_settingsLoaded = true;
+
+ m_state->loadFromSettings();
+}
+
+void InspectorController::startTimelineProfiler()
+{
+ if (!enabled())
+ return;
+
+ if (m_timelineAgent)
+ return;
+
+ m_timelineAgent = new InspectorTimelineAgent(m_frontend.get());
+ if (m_frontend)
+ m_frontend->timelineProfilerWasStarted();
+
+ m_state->setBoolean(InspectorState::timelineProfilerEnabled, true);
+}
+
+void InspectorController::stopTimelineProfiler()
+{
+ if (!enabled())
+ return;
+
+ if (!m_timelineAgent)
+ return;
+
+ m_timelineAgent = 0;
+ if (m_frontend)
+ m_frontend->timelineProfilerWasStopped();
+
+ m_state->setBoolean(InspectorState::timelineProfilerEnabled, false);
+}
+
+#if ENABLE(WORKERS)
+class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task {
+public:
+ static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorController::WorkerAction action)
+ {
+ return new PostWorkerNotificationToFrontendTask(worker, action);
+ }
+
+private:
+ PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorController::WorkerAction action)
+ : m_worker(worker)
+ , m_action(action)
+ {
+ }
+
+ virtual void performTask(ScriptExecutionContext* scriptContext)
+ {
+ if (InspectorController* inspector = scriptContext->inspectorController())
+ inspector->postWorkerNotificationToFrontend(*m_worker, m_action);
+ }
+
+private:
+ RefPtr<InspectorWorkerResource> m_worker;
+ InspectorController::WorkerAction m_action;
+};
+
+void InspectorController::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorController::WorkerAction action)
+{
+ if (!m_frontend)
+ return;
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ switch (action) {
+ case InspectorController::WorkerCreated:
+ m_frontend->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker());
+ break;
+ case InspectorController::WorkerDestroyed:
+ m_frontend->didDestroyWorker(worker.id());
+ break;
+ }
+#endif
+}
+
+void InspectorController::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, InspectorController::WorkerCreated));
+}
+
+void InspectorController::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, InspectorController::WorkerDestroyed));
+ m_workers.remove(workerResource);
+}
+#endif // ENABLE(WORKERS)
+
+#if ENABLE(DATABASE)
+void InspectorController::didOpenDatabase(PassRefPtr<Database> database, const String& domain, const String& name, const String& version)
+{
+ if (!enabled())
+ return;
+
+ RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
+
+ m_databaseResources.set(resource->id(), resource);
+
+ // Resources are only bound while visible.
+ if (m_frontend)
+ resource->bind(m_frontend.get());
+}
+#endif
+
+void InspectorController::getCookies(RefPtr<InspectorArray>* cookies, WTF::String* cookiesString)
+{
+ // If we can get raw cookies.
+ ListHashSet<Cookie> rawCookiesList;
+
+ // If we can't get raw cookies - fall back to String representation
+ String stringCookiesList;
+
+ // Return value to getRawCookies should be the same for every call because
+ // the return value is platform/network backend specific, and the call will
+ // always return the same true/false value.
+ bool rawCookiesImplemented = false;
+
+ for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext(m_inspectedPage->mainFrame())) {
+ Document* document = frame->document();
+ const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
+ CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
+ for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
+ Vector<Cookie> docCookiesList;
+ rawCookiesImplemented = getRawCookies(document, KURL(ParsedURLString, it->second->url()), docCookiesList);
+
+ if (!rawCookiesImplemented) {
+ // FIXME: We need duplication checking for the String representation of cookies.
+ ExceptionCode ec = 0;
+ stringCookiesList += document->cookie(ec);
+ // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
+ // because "document" is the document of the main frame of the page.
+ ASSERT(!ec);
+ } else {
+ int cookiesSize = docCookiesList.size();
+ for (int i = 0; i < cookiesSize; i++) {
+ if (!rawCookiesList.contains(docCookiesList[i]))
+ rawCookiesList.add(docCookiesList[i]);
+ }
+ }
+ }
+ }
+
+ if (rawCookiesImplemented)
+ *cookies = buildArrayForCookies(rawCookiesList);
+ else
+ *cookiesString = stringCookiesList;
+}
+
+PassRefPtr<InspectorArray> InspectorController::buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
+{
+ RefPtr<InspectorArray> cookies = InspectorArray::create();
+
+ ListHashSet<Cookie>::iterator end = cookiesList.end();
+ ListHashSet<Cookie>::iterator it = cookiesList.begin();
+ for (int i = 0; it != end; ++it, i++)
+ cookies->pushObject(buildObjectForCookie(*it));
+
+ return cookies;
+}
+
+PassRefPtr<InspectorObject> InspectorController::buildObjectForCookie(const Cookie& cookie)
+{
+ RefPtr<InspectorObject> value = InspectorObject::create();
+ value->setString("name", cookie.name);
+ value->setString("value", cookie.value);
+ value->setString("domain", cookie.domain);
+ value->setString("path", cookie.path);
+ value->setNumber("expires", cookie.expires);
+ value->setNumber("size", (cookie.name.length() + cookie.value.length()));
+ value->setBoolean("httpOnly", cookie.httpOnly);
+ value->setBoolean("secure", cookie.secure);
+ value->setBoolean("session", cookie.session);
+ return value;
+}
+
+void InspectorController::deleteCookie(const String& cookieName, const String& domain)
+{
+ for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext(m_inspectedPage->mainFrame())) {
+ Document* document = frame->document();
+ if (document->url().host() != domain)
+ continue;
+ const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
+ CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
+ for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it)
+ WebCore::deleteCookie(document, KURL(ParsedURLString, it->second->url()), cookieName);
+ }
+}
+
+#if ENABLE(DOM_STORAGE)
+void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame)
+{
+ if (!enabled())
+ return;
+
+ DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
+ for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
+ if (it->second->isSameHostAndType(frame, isLocalStorage))
+ return;
+
+ RefPtr<Storage> domStorage = Storage::create(frame, storageArea);
+ RefPtr<InspectorDOMStorageResource> resource = InspectorDOMStorageResource::create(domStorage.get(), isLocalStorage, frame);
+
+ m_domStorageResources.set(resource->id(), resource);
+
+ // Resources are only bound while visible.
+ if (m_frontend)
+ resource->bind(m_frontend.get());
+}
+#endif
+
+#if ENABLE(WEB_SOCKETS)
+void InspectorController::didCreateWebSocket(unsigned long identifier, const KURL& requestURL, const KURL& documentURL)
+{
+ if (!enabled())
+ return;
+ ASSERT(m_inspectedPage);
+
+ if (m_resourceAgent)
+ m_resourceAgent->didCreateWebSocket(identifier, requestURL);
+ UNUSED_PARAM(documentURL);
+}
+
+void InspectorController::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request)
+{
+ if (m_resourceAgent)
+ m_resourceAgent->willSendWebSocketHandshakeRequest(identifier, request);
+}
+
+void InspectorController::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response)
+{
+ if (m_resourceAgent)
+ m_resourceAgent->didReceiveWebSocketHandshakeResponse(identifier, response);
+}
+
+void InspectorController::didCloseWebSocket(unsigned long identifier)
+{
+ if (m_resourceAgent)
+ m_resourceAgent->didCloseWebSocket(identifier);
+}
+#endif // ENABLE(WEB_SOCKETS)
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+void InspectorController::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
+{
+ if (!enabled())
+ return;
+ m_profilerAgent->addProfile(prpProfile, lineNumber, sourceURL);
+}
+
+void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
+{
+ m_profilerAgent->addProfileFinishedMessageToConsole(prpProfile, lineNumber, sourceURL);
+}
+
+void InspectorController::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
+{
+ m_profilerAgent->addStartProfilingMessageToConsole(title, lineNumber, sourceURL);
+}
+
+
+bool InspectorController::isRecordingUserInitiatedProfile() const
+{
+ return m_profilerAgent->isRecordingUserInitiatedProfile();
+}
+
+String InspectorController::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
+{
+ return m_profilerAgent->getCurrentUserInitiatedProfileName(incrementProfileNumber);
+}
+
+void InspectorController::startUserInitiatedProfiling()
+{
+ if (!enabled())
+ return;
+ m_profilerAgent->startUserInitiatedProfiling();
+ m_state->setBoolean(InspectorState::userInitiatedProfiling, true);
+}
+
+void InspectorController::stopUserInitiatedProfiling()
+{
+ if (!enabled())
+ return;
+ m_profilerAgent->stopUserInitiatedProfiling();
+ m_state->setBoolean(InspectorState::userInitiatedProfiling, false);
+}
+
+bool InspectorController::profilerEnabled() const
+{
+ return enabled() && m_profilerAgent->enabled();
+}
+
+void InspectorController::enableProfiler(bool always, bool skipRecompile)
+{
+ if (always)
+ m_state->setBoolean(InspectorState::profilerAlwaysEnabled, true);
+ m_profilerAgent->enable(skipRecompile);
+}
+
+void InspectorController::disableProfiler(bool always)
+{
+ if (always)
+ m_state->setBoolean(InspectorState::profilerAlwaysEnabled, false);
+ m_profilerAgent->disable();
+}
+#endif
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+void InspectorController::enableDebuggerFromFrontend(bool always)
+{
+ ASSERT(!debuggerEnabled());
+ if (always)
+ m_state->setBoolean(InspectorState::debuggerAlwaysEnabled, true);
+
+ ASSERT(m_inspectedPage);
+
+ m_debuggerAgent = InspectorDebuggerAgent::create(this, m_frontend.get());
+ restoreStickyBreakpoints();
+
+ m_frontend->debuggerWasEnabled();
+}
+
+void InspectorController::enableDebugger()
+{
+ if (!enabled())
+ return;
+
+ if (debuggerEnabled())
+ return;
+
+ if (!m_frontend)
+ m_attachDebuggerWhenShown = true;
+ else {
+ m_frontend->attachDebuggerWhenShown();
+ m_attachDebuggerWhenShown = false;
+ }
+}
+
+void InspectorController::disableDebugger(bool always)
+{
+ if (!enabled())
+ return;
+
+ if (always)
+ m_state->setBoolean(InspectorState::debuggerAlwaysEnabled, false);
+
+ ASSERT(m_inspectedPage);
+
+ m_debuggerAgent.clear();
+
+ m_attachDebuggerWhenShown = false;
+
+ if (m_frontend)
+ m_frontend->debuggerWasDisabled();
+}
+
+void InspectorController::resume()
+{
+ if (m_debuggerAgent)
+ m_debuggerAgent->resume();
+}
+
+void InspectorController::setStickyBreakpoints(PassRefPtr<InspectorObject> breakpoints)
+{
+ m_state->setObject(InspectorState::stickyBreakpoints, breakpoints);
+}
+
+void InspectorController::restoreStickyBreakpoints()
+{
+ m_eventListenerBreakpoints.clear();
+ m_XHRBreakpoints.clear();
+ m_hasXHRBreakpointWithEmptyURL = false;
+
+ RefPtr<InspectorObject> allBreakpoints = m_state->getObject(InspectorState::stickyBreakpoints);
+ KURL url = m_inspectedPage->mainFrame()->loader()->url();
+ url.removeFragmentIdentifier();
+ RefPtr<InspectorArray> breakpoints = allBreakpoints->getArray(url);
+ if (!breakpoints)
+ return;
+ for (unsigned i = 0; i < breakpoints->length(); ++i)
+ restoreStickyBreakpoint(breakpoints->get(i)->asObject());
+}
+
+void InspectorController::restoreStickyBreakpoint(PassRefPtr<InspectorObject> breakpoint)
+{
+ DEFINE_STATIC_LOCAL(String, eventListenerBreakpointType, ("EventListener"));
+ DEFINE_STATIC_LOCAL(String, javaScriptBreakpointType, ("JS"));
+ DEFINE_STATIC_LOCAL(String, xhrBreakpointType, ("XHR"));
+
+ if (!breakpoint)
+ return;
+ String type;
+ if (!breakpoint->getString("type", &type))
+ return;
+ bool enabled;
+ if (!breakpoint->getBoolean("enabled", &enabled))
+ return;
+ RefPtr<InspectorObject> condition = breakpoint->getObject("condition");
+ if (!condition)
+ return;
+
+ if (type == eventListenerBreakpointType) {
+ if (!enabled)
+ return;
+ String eventName;
+ if (condition->getString("eventName", &eventName))
+ setEventListenerBreakpoint(eventName);
+ } else if (type == javaScriptBreakpointType) {
+ String url;
+ if (!condition->getString("url", &url))
+ return;
+ double lineNumber;
+ if (!condition->getNumber("lineNumber", &lineNumber))
+ return;
+ String javaScriptCondition;
+ if (!condition->getString("condition", &javaScriptCondition))
+ return;
+ if (m_debuggerAgent)
+ m_debuggerAgent->setStickyBreakpoint(url, static_cast<unsigned>(lineNumber), javaScriptCondition, enabled);
+ } else if (type == xhrBreakpointType) {
+ if (!enabled)
+ return;
+ String url;
+ if (condition->getString("url", &url))
+ setXHRBreakpoint(url);
+ }
+}
+
+void InspectorController::setEventListenerBreakpoint(const String& eventName)
+{
+ m_eventListenerBreakpoints.add(eventName);
+}
+
+void InspectorController::removeEventListenerBreakpoint(const String& eventName)
+{
+ m_eventListenerBreakpoints.remove(eventName);
+}
+
+bool InspectorController::hasEventListenerBreakpoint(const String& eventName)
+{
+ return m_eventListenerBreakpoints.contains(eventName);
+}
+
+void InspectorController::setXHRBreakpoint(const String& url)
+{
+ if (url.isEmpty())
+ m_hasXHRBreakpointWithEmptyURL = true;
+ else
+ m_XHRBreakpoints.add(url);
+}
+
+void InspectorController::removeXHRBreakpoint(const String& url)
+{
+ if (url.isEmpty())
+ m_hasXHRBreakpointWithEmptyURL = false;
+ else
+ m_XHRBreakpoints.remove(url);
+}
+
+bool InspectorController::hasXHRBreakpoint(const String& url, String* breakpointURL)
+{
+ if (m_hasXHRBreakpointWithEmptyURL) {
+ *breakpointURL = "";
+ return true;
+ }
+ for (HashSet<String>::iterator it = m_XHRBreakpoints.begin(); it != m_XHRBreakpoints.end(); ++it) {
+ if (url.contains(*it)) {
+ *breakpointURL = *it;
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
+void InspectorController::setInjectedScriptSource(const String& source)
+{
+ injectedScriptHost()->setInjectedScriptSource(source);
+}
+
+void InspectorController::dispatchOnInjectedScript(long injectedScriptId, const String& methodName, const String& arguments, RefPtr<InspectorValue>* result, bool* hadException)
+{
+ if (!m_frontend)
+ return;
+
+ // FIXME: explicitly pass injectedScriptId along with node id to the frontend.
+ bool injectedScriptIdIsNodeId = injectedScriptId <= 0;
+
+ InjectedScript injectedScript;
+ if (injectedScriptIdIsNodeId)
+ injectedScript = injectedScriptForNodeId(-injectedScriptId);
+ else
+ injectedScript = injectedScriptHost()->injectedScriptForId(injectedScriptId);
+
+ if (injectedScript.hasNoValue())
+ return;
+
+ injectedScript.dispatch(methodName, arguments, result, hadException);
+}
+
+void InspectorController::releaseWrapperObjectGroup(long injectedScriptId, const String& objectGroup)
+{
+ injectedScriptHost()->releaseWrapperObjectGroup(injectedScriptId, objectGroup);
+}
+
+void InspectorController::evaluateForTestInFrontend(long callId, const String& script)
+{
+ if (m_frontend)
+ m_frontend->evaluateForTestInFrontend(callId, script);
+ else
+ m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
+}
+
+void InspectorController::didEvaluateForTestInFrontend(long callId, const String& jsonResult)
+{
+ ScriptState* scriptState = scriptStateFromPage(debuggerWorld(), m_inspectedPage);
+ ScriptObject window;
+ ScriptGlobalObject::get(scriptState, "window", window);
+ ScriptFunctionCall function(window, "didEvaluateForTestInFrontend");
+ function.appendArgument(callId);
+ function.appendArgument(jsonResult);
+ function.call();
+}
+
+static Path quadToPath(const FloatQuad& quad)
+{
+ Path quadPath;
+ quadPath.moveTo(quad.p1());
+ quadPath.addLineTo(quad.p2());
+ quadPath.addLineTo(quad.p3());
+ quadPath.addLineTo(quad.p4());
+ quadPath.closeSubpath();
+ return quadPath;
+}
+
+static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, const Color& fillColor)
+{
+ static const int outlineThickness = 2;
+ static const Color outlineColor(62, 86, 180, 228);
+
+ Path quadPath = quadToPath(quad);
+
+ // Clip out the quad, then draw with a 2px stroke to get a pixel
+ // of outline (because inflating a quad is hard)
+ {
+ context.save();
+ context.clipOut(quadPath);
+
+ context.setStrokeThickness(outlineThickness);
+ context.setStrokeColor(outlineColor, ColorSpaceDeviceRGB);
+ context.strokePath(quadPath);
+
+ context.restore();
+ }
+
+ // Now do the fill
+ context.setFillColor(fillColor, ColorSpaceDeviceRGB);
+ context.fillPath(quadPath);
+}
+
+static void drawOutlinedQuadWithClip(GraphicsContext& context, const FloatQuad& quad, const FloatQuad& clipQuad, const Color& fillColor)
+{
+ context.save();
+ Path clipQuadPath = quadToPath(clipQuad);
+ context.clipOut(clipQuadPath);
+ drawOutlinedQuad(context, quad, fillColor);
+ context.restore();
+}
+
+static void drawHighlightForBox(GraphicsContext& context, const FloatQuad& contentQuad, const FloatQuad& paddingQuad, const FloatQuad& borderQuad, const FloatQuad& marginQuad)
+{
+ static const Color contentBoxColor(125, 173, 217, 128);
+ static const Color paddingBoxColor(125, 173, 217, 160);
+ static const Color borderBoxColor(125, 173, 217, 192);
+ static const Color marginBoxColor(125, 173, 217, 228);
+
+ if (marginQuad != borderQuad)
+ drawOutlinedQuadWithClip(context, marginQuad, borderQuad, marginBoxColor);
+ if (borderQuad != paddingQuad)
+ drawOutlinedQuadWithClip(context, borderQuad, paddingQuad, borderBoxColor);
+ if (paddingQuad != contentQuad)
+ drawOutlinedQuadWithClip(context, paddingQuad, contentQuad, paddingBoxColor);
+
+ drawOutlinedQuad(context, contentQuad, contentBoxColor);
+}
+
+static void drawHighlightForLineBoxesOrSVGRenderer(GraphicsContext& context, const Vector<FloatQuad>& lineBoxQuads)
+{
+ static const Color lineBoxColor(125, 173, 217, 128);
+
+ for (size_t i = 0; i < lineBoxQuads.size(); ++i)
+ drawOutlinedQuad(context, lineBoxQuads[i], lineBoxColor);
+}
+
+static inline void convertFromFrameToMainFrame(Frame* frame, IntRect& rect)
+{
+ rect = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(rect));
+}
+
+static inline IntSize frameToMainFrameOffset(Frame* frame)
+{
+ IntPoint mainFramePoint = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint()));
+ return mainFramePoint - IntPoint();
+}
+
+void InspectorController::drawNodeHighlight(GraphicsContext& context) const
+{
+ if (!m_highlightedNode)
+ return;
+
+ RenderObject* renderer = m_highlightedNode->renderer();
+ Frame* containingFrame = m_highlightedNode->document()->frame();
+ if (!renderer || !containingFrame)
+ return;
+
+ IntSize mainFrameOffset = frameToMainFrameOffset(containingFrame);
+ IntRect boundingBox = renderer->absoluteBoundingBoxRect(true);
+ boundingBox.move(mainFrameOffset);
+
+ IntRect titleReferenceBox = boundingBox;
+
+ ASSERT(m_inspectedPage);
+
+ FrameView* view = m_inspectedPage->mainFrame()->view();
+ FloatRect overlayRect = view->visibleContentRect();
+ if (!overlayRect.contains(boundingBox) && !boundingBox.contains(enclosingIntRect(overlayRect)))
+ overlayRect = view->visibleContentRect();
+ context.translate(-overlayRect.x(), -overlayRect.y());
+
+ // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
+#if ENABLE(SVG)
+ bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot();
+#else
+ bool isSVGRenderer = false;
+#endif
+
+ if (renderer->isBox() && !isSVGRenderer) {
+ RenderBox* renderBox = toRenderBox(renderer);
+
+ IntRect contentBox = renderBox->contentBoxRect();
+
+ IntRect paddingBox(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
+ contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
+ IntRect borderBox(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
+ paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
+ IntRect marginBox(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
+ borderBox.width() + renderBox->marginLeft() + renderBox->marginRight(), borderBox.height() + renderBox->marginTop() + renderBox->marginBottom());
+
+ titleReferenceBox = marginBox;
+ titleReferenceBox.move(mainFrameOffset);
+ titleReferenceBox.move(boundingBox.x(), boundingBox.y());
+
+ FloatQuad absContentQuad = renderBox->localToAbsoluteQuad(FloatRect(contentBox));
+ FloatQuad absPaddingQuad = renderBox->localToAbsoluteQuad(FloatRect(paddingBox));
+ FloatQuad absBorderQuad = renderBox->localToAbsoluteQuad(FloatRect(borderBox));
+ FloatQuad absMarginQuad = renderBox->localToAbsoluteQuad(FloatRect(marginBox));
+
+ absContentQuad.move(mainFrameOffset);
+ absPaddingQuad.move(mainFrameOffset);
+ absBorderQuad.move(mainFrameOffset);
+ absMarginQuad.move(mainFrameOffset);
+
+ drawHighlightForBox(context, absContentQuad, absPaddingQuad, absBorderQuad, absMarginQuad);
+ } else if (renderer->isRenderInline() || isSVGRenderer) {
+ // FIXME: We should show margins/padding/border for inlines.
+ Vector<FloatQuad> lineBoxQuads;
+ renderer->absoluteQuads(lineBoxQuads);
+ for (unsigned i = 0; i < lineBoxQuads.size(); ++i)
+ lineBoxQuads[i] += mainFrameOffset;
+
+ drawHighlightForLineBoxesOrSVGRenderer(context, lineBoxQuads);
+ }
+
+ // Draw node title if necessary.
+
+ if (!m_highlightedNode->isElementNode())
+ return;
+
+ WebCore::Settings* settings = containingFrame->settings();
+ drawElementTitle(context, titleReferenceBox, overlayRect, settings);
+}
+
+void InspectorController::drawElementTitle(GraphicsContext& context, const IntRect& boundingBox, const FloatRect& overlayRect, WebCore::Settings* settings) const
+{
+ static const int rectInflatePx = 4;
+ static const int fontHeightPx = 12;
+ static const int borderWidthPx = 1;
+ static const Color tooltipBackgroundColor(255, 255, 194, 255);
+ static const Color tooltipBorderColor(Color::black);
+ static const Color tooltipFontColor(Color::black);
+
+ Element* element = static_cast<Element*>(m_highlightedNode.get());
+ bool isXHTML = element->document()->isXHTMLDocument();
+ String nodeTitle = isXHTML ? element->nodeName() : element->nodeName().lower();
+ const AtomicString& idValue = element->getIdAttribute();
+ if (!idValue.isNull() && !idValue.isEmpty()) {
+ nodeTitle += "#";
+ nodeTitle += idValue;
+ }
+ if (element->hasClass() && element->isStyledElement()) {
+ const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
+ size_t classNameCount = classNamesString.size();
+ if (classNameCount) {
+ HashSet<AtomicString> usedClassNames;
+ for (size_t i = 0; i < classNameCount; ++i) {
+ const AtomicString& className = classNamesString[i];
+ if (usedClassNames.contains(className))
+ continue;
+ usedClassNames.add(className);
+ nodeTitle += ".";
+ nodeTitle += className;
+ }
+ }
+ }
+
+ Element* highlightedElement = m_highlightedNode->isElementNode() ? static_cast<Element*>(m_highlightedNode.get()) : 0;
+ nodeTitle += " [";
+ nodeTitle += String::number(highlightedElement ? highlightedElement->offsetWidth() : boundingBox.width());
+ nodeTitle.append(static_cast<UChar>(0x00D7)); // &times;
+ nodeTitle += String::number(highlightedElement ? highlightedElement->offsetHeight() : boundingBox.height());
+ nodeTitle += "]";
+
+ FontDescription desc;
+ FontFamily family;
+ family.setFamily(settings->fixedFontFamily());
+ desc.setFamily(family);
+ desc.setComputedSize(fontHeightPx);
+ Font font = Font(desc, 0, 0);
+ font.update(0);
+
+ TextRun nodeTitleRun(nodeTitle);
+ IntPoint titleBasePoint = boundingBox.bottomLeft();
+ titleBasePoint.move(rectInflatePx, rectInflatePx);
+ IntRect titleRect = enclosingIntRect(font.selectionRectForText(nodeTitleRun, titleBasePoint, fontHeightPx));
+ titleRect.inflate(rectInflatePx);
+
+ // The initial offsets needed to compensate for a 1px-thick border stroke (which is not a part of the rectangle).
+ int dx = -borderWidthPx;
+ int dy = borderWidthPx;
+
+ // If the tip sticks beyond the right of overlayRect, right-align the tip with the said boundary.
+ if (titleRect.right() > overlayRect.right())
+ dx = overlayRect.right() - titleRect.right();
+
+ // If the tip sticks beyond the left of overlayRect, left-align the tip with the said boundary.
+ if (titleRect.x() + dx < overlayRect.x())
+ dx = overlayRect.x() - titleRect.x() - borderWidthPx;
+
+ // If the tip sticks beyond the bottom of overlayRect, show the tip at top of bounding box.
+ if (titleRect.bottom() > overlayRect.bottom()) {
+ dy = boundingBox.y() - titleRect.bottom() - borderWidthPx;
+ // If the tip still sticks beyond the bottom of overlayRect, bottom-align the tip with the said boundary.
+ if (titleRect.bottom() + dy > overlayRect.bottom())
+ dy = overlayRect.bottom() - titleRect.bottom();
+ }
+
+ // If the tip sticks beyond the top of overlayRect, show the tip at top of overlayRect.
+ if (titleRect.y() + dy < overlayRect.y())
+ dy = overlayRect.y() - titleRect.y() + borderWidthPx;
+
+ titleRect.move(dx, dy);
+ context.setStrokeColor(tooltipBorderColor, ColorSpaceDeviceRGB);
+ context.setStrokeThickness(borderWidthPx);
+ context.setFillColor(tooltipBackgroundColor, ColorSpaceDeviceRGB);
+ context.drawRect(titleRect);
+ context.setFillColor(tooltipFontColor, ColorSpaceDeviceRGB);
+ context.drawText(font, nodeTitleRun, IntPoint(titleRect.x() + rectInflatePx, titleRect.y() + font.height()));
+}
+
+void InspectorController::openInInspectedWindow(const String& url)
+{
+ Frame* mainFrame = m_inspectedPage->mainFrame();
+
+ FrameLoadRequest request(mainFrame->document()->securityOrigin(), ResourceRequest(), "_blank");
+
+ bool created;
+ WindowFeatures windowFeatures;
+ Frame* newFrame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
+ if (!newFrame)
+ return;
+
+ UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
+ newFrame->loader()->setOpener(mainFrame);
+ newFrame->page()->setOpenedByDOM();
+ newFrame->loader()->changeLocation(mainFrame->document()->securityOrigin(), newFrame->loader()->completeURL(url), "", false, false);
+}
+
+void InspectorController::count(const String& title, unsigned lineNumber, const String& sourceID)
+{
+ String identifier = makeString(title, '@', sourceID, ':', String::number(lineNumber));
+ HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
+ int count;
+ if (it == m_counts.end())
+ count = 1;
+ else {
+ count = it->second + 1;
+ m_counts.remove(it);
+ }
+
+ m_counts.add(identifier, count);
+
+ String message = makeString(title, ": ", String::number(count));
+ addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceID);
+}
+
+void InspectorController::startTiming(const String& title)
+{
+ m_times.add(title, currentTime() * 1000);
+}
+
+bool InspectorController::stopTiming(const String& title, double& elapsed)
+{
+ HashMap<String, double>::iterator it = m_times.find(title);
+ if (it == m_times.end())
+ return false;
+
+ double startTime = it->second;
+ m_times.remove(it);
+
+ elapsed = currentTime() * 1000 - startTime;
+ return true;
+}
+
+InjectedScript InspectorController::injectedScriptForNodeId(long id)
+{
+
+ Frame* frame = 0;
+ if (id) {
+ ASSERT(m_domAgent);
+ Node* node = m_domAgent->nodeForId(id);
+ if (node) {
+ Document* document = node->ownerDocument();
+ if (document)
+ frame = document->frame();
+ }
+ } else
+ frame = m_inspectedPage->mainFrame();
+
+ if (frame)
+ return m_injectedScriptHost->injectedScriptFor(mainWorldScriptState(frame));
+
+ return InjectedScript();
+}
+
+void InspectorController::addScriptToEvaluateOnLoad(const String& source)
+{
+ m_scriptsToEvaluateOnLoad.append(source);
+}
+
+void InspectorController::removeAllScriptsToEvaluateOnLoad()
+{
+ m_scriptsToEvaluateOnLoad.clear();
+}
+
+void InspectorController::setInspectorExtensionAPI(const String& source)
+{
+ m_inspectorExtensionAPI = source;
+}
+
+void InspectorController::reloadPage()
+{
+ // FIXME: Why do we set the user gesture indicator here?
+ UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
+ m_inspectedPage->mainFrame()->navigationScheduler()->scheduleRefresh();
+}
+
+void InspectorController::setExtraHeaders(PassRefPtr<InspectorObject> headers)
+{
+ m_extraHeaders = adoptPtr(new HTTPHeaderMap());
+ InspectorObject::const_iterator end = headers->end();
+ for (InspectorObject::const_iterator it = headers->begin(); it != end; ++it) {
+ String value;
+ if (!it->second->asString(&value))
+ continue;
+ m_extraHeaders->add(it->first, value);
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INSPECTOR)