diff options
Diffstat (limited to 'Source/WebCore/inspector/InspectorAgent.cpp')
-rw-r--r-- | Source/WebCore/inspector/InspectorAgent.cpp | 1269 |
1 files changed, 1269 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/InspectorAgent.cpp b/Source/WebCore/inspector/InspectorAgent.cpp new file mode 100644 index 0000000..9559099 --- /dev/null +++ b/Source/WebCore/inspector/InspectorAgent.cpp @@ -0,0 +1,1269 @@ +/* + * 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 "InspectorAgent.h" + +#if ENABLE(INSPECTOR) + +#include "CachedResource.h" +#include "CachedResourceLoader.h" +#include "Chrome.h" +#include "Cookie.h" +#include "CookieJar.h" +#include "DOMWindow.h" +#include "DOMWrapperWorld.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 "InspectorBrowserDebuggerAgent.h" +#include "InspectorCSSAgent.h" +#include "InspectorClient.h" +#include "InspectorConsoleAgent.h" +#include "InspectorController.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 "InspectorRuntimeAgent.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 "TextRun.h" +#include "UserGestureIndicator.h" +#include "WindowFeatures.h" +#include <wtf/CurrentTime.h> +#include <wtf/ListHashSet.h> +#include <wtf/RefCounted.h> +#include <wtf/StdLibExtras.h> +#include <wtf/UnusedParam.h> +#include <wtf/text/StringConcatenate.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 + +using namespace std; + +namespace WebCore { + +namespace InspectorAgentState { +static const char searchingForNode[] = "searchingForNode"; +static const char timelineProfilerEnabled[] = "timelineProfilerEnabled"; +static const char userInitiatedProfiling[] = "userInitiatedProfiling"; +static const char debuggerEnabled[] = "debuggerEnabled"; +static const char profilerEnabled[] = "profilerEnabled"; +} + +static const char scriptsPanelName[] = "scripts"; +static const char consolePanelName[] = "console"; +static const char profilesPanelName[] = "profiles"; + +InspectorAgent::InspectorAgent(Page* page, InspectorClient* client) + : m_inspectedPage(page) + , m_client(client) + , m_frontend(0) + , m_cssAgent(new InspectorCSSAgent()) + , m_state(new InspectorState(client)) + , m_injectedScriptHost(InjectedScriptHost::create(this)) + , m_consoleAgent(new InspectorConsoleAgent(this)) +#if ENABLE(JAVASCRIPT_DEBUGGER) + , m_profilerAgent(InspectorProfilerAgent::create(this)) +#endif +{ + ASSERT_ARG(page, page); + ASSERT_ARG(client, client); + InspectorInstrumentation::bindInspectorAgent(m_inspectedPage, this); +} + +InspectorAgent::~InspectorAgent() +{ + // These should have been cleared in inspectedPageDestroyed(). + ASSERT(!m_client); + ASSERT(!m_inspectedPage); + ASSERT(!m_highlightedNode); +} + +void InspectorAgent::inspectedPageDestroyed() +{ + if (m_frontend) + m_frontend->disconnectFromBackend(); + + hideHighlight(); + +#if ENABLE(JAVASCRIPT_DEBUGGER) + m_debuggerAgent.clear(); + m_browserDebuggerAgent.clear(); +#endif + + ASSERT(m_inspectedPage); + InspectorInstrumentation::unbindInspectorAgent(m_inspectedPage); + m_inspectedPage = 0; + + releaseFrontendLifetimeAgents(); + m_injectedScriptHost->disconnectController(); + + m_client->inspectorDestroyed(); + m_client = 0; +} + +bool InspectorAgent::searchingForNodeInPage() const +{ + return m_state->getBoolean(InspectorAgentState::searchingForNode); +} + +void InspectorAgent::restoreInspectorStateFromCookie(const String& inspectorStateCookie) +{ + m_state = new InspectorState(m_client, inspectorStateCookie); + + m_frontend->frontendReused(); + m_frontend->inspectedURLChanged(inspectedURL().string()); + pushDataCollectedOffline(); + + m_resourceAgent = InspectorResourceAgent::restore(m_inspectedPage, m_state.get(), m_frontend); + m_timelineAgent = InspectorTimelineAgent::restore(m_state.get(), m_frontend); + +#if ENABLE(JAVASCRIPT_DEBUGGER) + restoreDebugger(false); + restoreProfiler(ProfilerRestoreResetAgent); + if (m_state->getBoolean(InspectorAgentState::userInitiatedProfiling)) + startUserInitiatedProfiling(); +#endif +} + +void InspectorAgent::inspect(Node* node) +{ + if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE) + node = node->parentNode(); + m_nodeToFocus = node; + + if (!m_frontend) + return; + + focusNode(); +} + +void InspectorAgent::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 InspectorAgent::highlight(Node* node) +{ + if (!enabled()) + return; + ASSERT_ARG(node, node); + m_highlightedNode = node; + m_client->highlight(node); +} + +void InspectorAgent::highlightDOMNode(long nodeId) +{ + Node* node = 0; + if (m_domAgent && (node = m_domAgent->nodeForId(nodeId))) + highlight(node); +} + +void InspectorAgent::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 InspectorAgent::hideHighlight() +{ + if (!enabled()) + return; + m_highlightedNode = 0; + m_client->hideHighlight(); +} + +void InspectorAgent::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); +} + +bool InspectorAgent::handleMousePress() +{ + if (!enabled() || !searchingForNodeInPage()) + return false; + + if (m_highlightedNode) { + RefPtr<Node> node = m_highlightedNode; + setSearchingForNode(false); + inspect(node.get()); + } + return true; +} + +void InspectorAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world) +{ + if (world != mainThreadNormalWorld()) + return; + + 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 InspectorAgent::setSearchingForNode(bool enabled) +{ + if (searchingForNodeInPage() == enabled) + return; + m_state->setBoolean(InspectorAgentState::searchingForNode, enabled); + if (!enabled) + hideHighlight(); +} + +void InspectorAgent::setSearchingForNode(bool enabled, bool* newState) +{ + *newState = enabled; + setSearchingForNode(enabled); +} + +void InspectorAgent::setFrontend(InspectorFrontend* inspectorFrontend) +{ + m_frontend = inspectorFrontend; + createFrontendLifetimeAgents(); + + m_cssAgent->setDOMAgent(m_domAgent.get()); + m_consoleAgent->setFrontend(m_frontend); + + // Initialize Web Inspector title. + m_frontend->inspectedURLChanged(inspectedURL().string()); +} + +void InspectorAgent::disconnectFrontend() +{ + if (!m_frontend) + return; + + // 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) + // If the window is being closed with the debugger enabled, + // remember this state to re-enable debugger on the next window + // opening. + disableDebugger(); +#endif + setSearchingForNode(false); + + hideHighlight(); + +#if ENABLE(JAVASCRIPT_DEBUGGER) + m_profilerAgent->setFrontend(0); + m_profilerAgent->stopUserInitiatedProfiling(true); +#endif + + m_consoleAgent->setFrontend(0); + + releaseFrontendLifetimeAgents(); + m_userAgentOverride = ""; +} + +InspectorResourceAgent* InspectorAgent::resourceAgent() +{ + if (!m_resourceAgent && m_frontend) + m_resourceAgent = InspectorResourceAgent::create(m_inspectedPage, m_state.get(), m_frontend); + return m_resourceAgent.get(); +} + +void InspectorAgent::createFrontendLifetimeAgents() +{ + m_domAgent = InspectorDOMAgent::create(m_injectedScriptHost.get(), m_frontend); + m_runtimeAgent = InspectorRuntimeAgent::create(m_injectedScriptHost.get()); + +#if ENABLE(DATABASE) + m_databaseAgent = InspectorDatabaseAgent::create(&m_databaseResources, m_frontend); +#endif + +#if ENABLE(DOM_STORAGE) + m_domStorageAgent = InspectorDOMStorageAgent::create(&m_domStorageResources, m_frontend); +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + m_applicationCacheAgent = new InspectorApplicationCacheAgent(m_inspectedPage->mainFrame()->loader()->documentLoader(), m_frontend); +#endif +} + +void InspectorAgent::releaseFrontendLifetimeAgents() +{ + m_resourceAgent.clear(); + m_runtimeAgent.clear(); + m_timelineAgent.clear(); + +#if ENABLE(DATABASE) + m_databaseAgent.clear(); +#endif + +#if ENABLE(DOM_STORAGE) + m_domStorageAgent.clear(); +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + m_applicationCacheAgent.clear(); +#endif + + // This should be invoked prior to m_domAgent destruction. + m_cssAgent->setDOMAgent(0); + m_domAgent.clear(); +} + +void InspectorAgent::populateScriptObjects() +{ + ASSERT(m_frontend); + if (!m_frontend) + return; + +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (m_profilerAgent->enabled()) + m_frontend->profilerWasEnabled(); +#endif + + pushDataCollectedOffline(); + + if (m_nodeToFocus) + focusNode(); + + if (!m_requiredPanel.isEmpty()) { + m_frontend->showPanel(m_requiredPanel); + m_requiredPanel = ""; + } + + restoreDebugger(true); + restoreProfiler(ProfilerRestoreNoAction); + + // 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(); +} + +void InspectorAgent::pushDataCollectedOffline() +{ + m_domAgent->setDocument(m_inspectedPage->mainFrame()->document()); + +#if ENABLE(DATABASE) + DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end(); + for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) + it->second->bind(m_frontend); +#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); +#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 +} + +void InspectorAgent::restoreDebugger(bool eraseStickyBreakpoints) +{ + ASSERT(m_frontend); +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (m_state->getBoolean(InspectorAgentState::debuggerEnabled)) + enableDebugger(eraseStickyBreakpoints); +#endif +} + +void InspectorAgent::restoreProfiler(ProfilerRestoreAction action) +{ + ASSERT(m_frontend); +#if ENABLE(JAVASCRIPT_DEBUGGER) + m_profilerAgent->setFrontend(m_frontend); + if (m_state->getBoolean(InspectorAgentState::profilerEnabled)) + enableProfiler(); + if (action == ProfilerRestoreResetAgent) + m_profilerAgent->resetFrontendProfiles(); +#endif +} + +void InspectorAgent::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(); + m_consoleAgent->reset(); + + if (m_timelineAgent) + m_timelineAgent->didCommitLoad(); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (m_applicationCacheAgent) + m_applicationCacheAgent->didCommitLoad(loader); +#endif + +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (m_debuggerAgent) { + KURL url = inspectedURLWithoutFragment(); + m_debuggerAgent->inspectedURLChanged(url); + if (m_browserDebuggerAgent) + m_browserDebuggerAgent->inspectedURLChanged(url); + } +#endif + +#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) + m_profilerAgent->stopUserInitiatedProfiling(true); + m_profilerAgent->resetState(); +#endif + + 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_domAgent->setDocument(m_inspectedPage->mainFrame()->document()); + } +} + +void InspectorAgent::domContentLoadedEventFired(DocumentLoader* loader, const KURL& url) +{ + if (!enabled() || !isMainResourceLoader(loader, url)) + return; + + if (m_domAgent) + m_domAgent->mainFrameDOMContentLoaded(); + if (m_timelineAgent) + m_timelineAgent->didMarkDOMContentEvent(); + if (m_frontend) + m_frontend->domContentEventFired(currentTime()); +} + +void InspectorAgent::loadEventFired(DocumentLoader* loader, const KURL& url) +{ + if (!enabled()) + return; + + if (m_domAgent) + m_domAgent->loadEventFired(loader->frame()->document()); + + if (!isMainResourceLoader(loader, url)) + return; + + if (m_timelineAgent) + m_timelineAgent->didMarkLoadEvent(); + if (m_frontend) + m_frontend->loadEventFired(currentTime()); +} + +bool InspectorAgent::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl) +{ + return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL(); +} + +void InspectorAgent::setUserAgentOverride(const String& userAgent) +{ + m_userAgentOverride = userAgent; +} + +void InspectorAgent::applyUserAgentOverride(String* userAgent) const +{ + if (!m_userAgentOverride.isEmpty()) + *userAgent = m_userAgentOverride; +} + +void InspectorAgent::startTimelineProfiler() +{ + if (m_timelineAgent || !enabled() || !m_frontend) + return; + + m_timelineAgent = InspectorTimelineAgent::create(m_state.get(), m_frontend); +} + +void InspectorAgent::stopTimelineProfiler() +{ + m_timelineAgent.clear(); +} + +#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->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker()); + break; + case InspectorAgent::WorkerDestroyed: + m_frontend->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(DATABASE) +void InspectorAgent::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); +} +#endif + +void InspectorAgent::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> InspectorAgent::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> InspectorAgent::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 InspectorAgent::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 InspectorAgent::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); +} +#endif + +#if ENABLE(WEB_SOCKETS) +void InspectorAgent::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 InspectorAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request) +{ + if (m_resourceAgent) + m_resourceAgent->willSendWebSocketHandshakeRequest(identifier, request); +} + +void InspectorAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response) +{ + if (m_resourceAgent) + m_resourceAgent->didReceiveWebSocketHandshakeResponse(identifier, response); +} + +void InspectorAgent::didCloseWebSocket(unsigned long identifier) +{ + if (m_resourceAgent) + m_resourceAgent->didCloseWebSocket(identifier); +} +#endif // ENABLE(WEB_SOCKETS) + +#if ENABLE(JAVASCRIPT_DEBUGGER) +bool InspectorAgent::isRecordingUserInitiatedProfile() const +{ + return m_profilerAgent->isRecordingUserInitiatedProfile(); +} + +void InspectorAgent::startUserInitiatedProfiling() +{ + if (!enabled()) + return; + m_profilerAgent->startUserInitiatedProfiling(); + m_state->setBoolean(InspectorAgentState::userInitiatedProfiling, true); +} + +void InspectorAgent::stopUserInitiatedProfiling() +{ + if (!enabled()) + return; + m_profilerAgent->stopUserInitiatedProfiling(); + m_state->setBoolean(InspectorAgentState::userInitiatedProfiling, false); + showPanel(profilesPanelName); +} + +bool InspectorAgent::profilerEnabled() const +{ + return enabled() && m_profilerAgent->enabled(); +} + +void InspectorAgent::enableProfiler() +{ + if (profilerEnabled()) + return; + m_state->setBoolean(InspectorAgentState::profilerEnabled, true); + m_profilerAgent->enable(false); +} + +void InspectorAgent::disableProfiler() +{ + m_state->setBoolean(InspectorAgentState::profilerEnabled, false); + m_profilerAgent->disable(); +} +#endif + +#if ENABLE(JAVASCRIPT_DEBUGGER) +void InspectorAgent::showAndEnableDebugger() +{ + if (!enabled()) + return; + + if (debuggerEnabled()) + return; + + if (!m_frontend) { + m_state->setBoolean(InspectorAgentState::debuggerEnabled, true); + showPanel(scriptsPanelName); + } else + enableDebugger(true); +} + +void InspectorAgent::enableDebugger(bool eraseStickyBreakpoints) +{ + if (debuggerEnabled()) + return; + m_state->setBoolean(InspectorAgentState::debuggerEnabled, true); + ASSERT(m_inspectedPage); + + m_debuggerAgent = InspectorDebuggerAgent::create(this, m_frontend, eraseStickyBreakpoints); + m_browserDebuggerAgent = InspectorBrowserDebuggerAgent::create(this, eraseStickyBreakpoints); + + m_frontend->debuggerWasEnabled(); +} + +void InspectorAgent::disableDebugger() +{ + if (!enabled()) + return; + ASSERT(m_inspectedPage); + m_debuggerAgent.clear(); + m_browserDebuggerAgent.clear(); + + if (m_frontend) { + m_frontend->debuggerWasDisabled(); + m_state->setBoolean(InspectorAgentState::debuggerEnabled, false); + } +} + +void InspectorAgent::resume() +{ + if (m_debuggerAgent) + m_debuggerAgent->resume(); +} +#endif + +void InspectorAgent::evaluateForTestInFrontend(long callId, const String& script) +{ + if (m_frontend) + m_frontend->evaluateForTestInFrontend(callId, script); + else + m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script)); +} + +void InspectorAgent::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 InspectorAgent::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 InspectorAgent::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)); // × + 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 = IntPoint(boundingBox.x(), boundingBox.maxY() - 1); + 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.maxX() > overlayRect.maxX()) + dx = overlayRect.maxX() - titleRect.maxX(); + + // 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.maxY() > overlayRect.maxY()) { + dy = boundingBox.y() - titleRect.maxY() - borderWidthPx; + // If the tip still sticks beyond the bottom of overlayRect, bottom-align the tip with the said boundary. + if (titleRect.maxY() + dy > overlayRect.maxY()) + dy = overlayRect.maxY() - titleRect.maxY(); + } + + // 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.fontMetrics().height())); +} + +void InspectorAgent::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 InspectorAgent::addScriptToEvaluateOnLoad(const String& source) +{ + m_scriptsToEvaluateOnLoad.append(source); +} + +void InspectorAgent::removeAllScriptsToEvaluateOnLoad() +{ + m_scriptsToEvaluateOnLoad.clear(); +} + +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; +} + +void InspectorAgent::reloadPage(bool ignoreCache) +{ + m_inspectedPage->mainFrame()->loader()->reload(ignoreCache); +} + +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_requiredPanel = panel; + return; + } + m_frontend->showPanel(panel); +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) |