summaryrefslogtreecommitdiffstats
path: root/WebKit/chromium/src/WebDevToolsAgentImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/chromium/src/WebDevToolsAgentImpl.cpp')
-rw-r--r--WebKit/chromium/src/WebDevToolsAgentImpl.cpp563
1 files changed, 563 insertions, 0 deletions
diff --git a/WebKit/chromium/src/WebDevToolsAgentImpl.cpp b/WebKit/chromium/src/WebDevToolsAgentImpl.cpp
new file mode 100644
index 0000000..9ce35b4
--- /dev/null
+++ b/WebKit/chromium/src/WebDevToolsAgentImpl.cpp
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER OR 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 "WebDevToolsAgentImpl.h"
+
+#include "BoundObject.h"
+#include "DebuggerAgentImpl.h"
+#include "DebuggerAgentManager.h"
+#include "Document.h"
+#include "EventListener.h"
+#include "InjectedScriptHost.h"
+#include "InspectorBackend.h"
+#include "InspectorController.h"
+#include "InspectorFrontend.h"
+#include "InspectorResource.h"
+#include "Node.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "ProfilerAgentImpl.h"
+#include "ResourceError.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "ScriptObject.h"
+#include "ScriptState.h"
+#include "ScriptValue.h"
+#include "V8Binding.h"
+#include "V8InspectorBackend.h"
+#include "V8Proxy.h"
+#include "V8Utilities.h"
+#include "WebDataSource.h"
+#include "WebDevToolsAgentClient.h"
+#include "WebDevToolsMessageData.h"
+#include "WebFrameImpl.h"
+#include "WebString.h"
+#include "WebURL.h"
+#include "WebURLError.h"
+#include "WebURLRequest.h"
+#include "WebURLResponse.h"
+#include "WebViewImpl.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+
+using WebCore::Document;
+using WebCore::DocumentLoader;
+using WebCore::FrameLoader;
+using WebCore::InjectedScriptHost;
+using WebCore::InspectorBackend;
+using WebCore::InspectorController;
+using WebCore::InspectorFrontend;
+using WebCore::InspectorResource;
+using WebCore::Node;
+using WebCore::Page;
+using WebCore::ResourceError;
+using WebCore::ResourceRequest;
+using WebCore::ResourceResponse;
+using WebCore::SafeAllocation;
+using WebCore::ScriptObject;
+using WebCore::ScriptState;
+using WebCore::ScriptValue;
+using WebCore::String;
+using WebCore::V8ClassIndex;
+using WebCore::V8DOMWrapper;
+using WebCore::V8InspectorBackend;
+using WebCore::V8Proxy;
+
+namespace WebKit {
+
+namespace {
+
+void InspectorBackendWeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter)
+{
+ InspectorBackend* backend = static_cast<InspectorBackend*>(parameter);
+ backend->deref();
+ object.Dispose();
+}
+
+void SetApuAgentEnabledInUtilityContext(v8::Handle<v8::Context> context, bool enabled)
+{
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(context);
+ v8::Handle<v8::Object> dispatcher = v8::Local<v8::Object>::Cast(
+ context->Global()->Get(v8::String::New("ApuAgentDispatcher")));
+ if (dispatcher.IsEmpty())
+ return;
+ dispatcher->Set(v8::String::New("enabled"), v8::Boolean::New(enabled));
+}
+
+// TODO(pfeldman): Make this public in WebDevToolsAgent API.
+static const char kApuAgentFeatureName[] = "apu-agent";
+
+// Keep these in sync with the ones in inject_dispatch.js.
+static const char kTimelineFeatureName[] = "timeline-profiler";
+static const char kResourceTrackingFeatureName[] = "resource-tracking";
+
+class IORPCDelegate : public DevToolsRPC::Delegate, public Noncopyable {
+public:
+ IORPCDelegate() { }
+ virtual ~IORPCDelegate() { }
+ virtual void sendRpcMessage(const WebDevToolsMessageData& data)
+ {
+ WebDevToolsAgentClient::sendMessageToFrontendOnIOThread(data);
+ }
+};
+
+} // namespace
+
+WebDevToolsAgentImpl::WebDevToolsAgentImpl(
+ WebViewImpl* webViewImpl,
+ WebDevToolsAgentClient* client)
+ : m_hostId(client->hostIdentifier())
+ , m_client(client)
+ , m_webViewImpl(webViewImpl)
+ , m_apuAgentEnabled(false)
+ , m_resourceTrackingWasEnabled(false)
+ , m_attached(false)
+{
+ m_debuggerAgentDelegateStub.set(new DebuggerAgentDelegateStub(this));
+ m_toolsAgentDelegateStub.set(new ToolsAgentDelegateStub(this));
+ m_apuAgentDelegateStub.set(new ApuAgentDelegateStub(this));
+}
+
+WebDevToolsAgentImpl::~WebDevToolsAgentImpl()
+{
+ DebuggerAgentManager::onWebViewClosed(m_webViewImpl);
+ disposeUtilityContext();
+}
+
+void WebDevToolsAgentImpl::disposeUtilityContext()
+{
+ if (!m_utilityContext.IsEmpty()) {
+ m_utilityContext.Dispose();
+ m_utilityContext.Clear();
+ }
+}
+
+void WebDevToolsAgentImpl::unhideResourcesPanelIfNecessary()
+{
+ InspectorController* ic = m_webViewImpl->page()->inspectorController();
+ ic->ensureResourceTrackingSettingsLoaded();
+ String command = String::format("[\"setResourcesPanelEnabled\", %s]",
+ ic->resourceTrackingEnabled() ? "true" : "false");
+ m_toolsAgentDelegateStub->dispatchOnClient(command);
+}
+
+void WebDevToolsAgentImpl::attach()
+{
+ if (m_attached)
+ return;
+ m_debuggerAgentImpl.set(
+ new DebuggerAgentImpl(m_webViewImpl,
+ m_debuggerAgentDelegateStub.get(),
+ this));
+ resetInspectorFrontendProxy();
+ unhideResourcesPanelIfNecessary();
+ // Allow controller to send messages to the frontend.
+ InspectorController* ic = inspectorController();
+
+ { // TODO(yurys): the source should have already been pushed by the frontend.
+ v8::HandleScope scope;
+ v8::Context::Scope contextScope(m_utilityContext);
+ v8::Handle<v8::Value> constructorValue = m_utilityContext->Global()->Get(
+ v8::String::New("injectedScriptConstructor"));
+ if (constructorValue->IsFunction()) {
+ String source = WebCore::toWebCoreString(constructorValue);
+ ic->injectedScriptHost()->setInjectedScriptSource("(" + source + ")");
+ }
+ }
+
+ ic->setWindowVisible(true, false);
+ m_attached = true;
+}
+
+void WebDevToolsAgentImpl::detach()
+{
+ // Prevent controller from sending messages to the frontend.
+ InspectorController* ic = m_webViewImpl->page()->inspectorController();
+ ic->hideHighlight();
+ ic->close();
+ disposeUtilityContext();
+ m_debuggerAgentImpl.set(0);
+ m_attached = false;
+ m_apuAgentEnabled = false;
+}
+
+void WebDevToolsAgentImpl::didNavigate()
+{
+ DebuggerAgentManager::onNavigate();
+}
+
+void WebDevToolsAgentImpl::didCommitProvisionalLoad(WebFrameImpl* webframe, bool isNewNavigation)
+{
+ if (!m_attached)
+ return;
+ WebDataSource* ds = webframe->dataSource();
+ const WebURLRequest& request = ds->request();
+ WebURL url = ds->hasUnreachableURL() ?
+ ds->unreachableURL() :
+ request.url();
+ if (!webframe->parent()) {
+ resetInspectorFrontendProxy();
+ m_toolsAgentDelegateStub->frameNavigate(WebCore::KURL(url).string());
+ SetApuAgentEnabledInUtilityContext(m_utilityContext, m_apuAgentEnabled);
+ unhideResourcesPanelIfNecessary();
+ }
+}
+
+void WebDevToolsAgentImpl::didClearWindowObject(WebFrameImpl* webframe)
+{
+ DebuggerAgentManager::setHostId(webframe, m_hostId);
+ if (m_attached) {
+ // Push context id into the client if it is already attached.
+ m_debuggerAgentDelegateStub->setContextId(m_hostId);
+ }
+}
+
+void WebDevToolsAgentImpl::forceRepaint()
+{
+ m_client->forceRepaint();
+}
+
+void WebDevToolsAgentImpl::dispatchOnInspectorController(int callId, const String& functionName, const String& jsonArgs)
+{
+ String result;
+ String exception;
+ result = m_debuggerAgentImpl->executeUtilityFunction(m_utilityContext, callId,
+ "InspectorControllerDispatcher", functionName, jsonArgs, false /* is sync */, &exception);
+ m_toolsAgentDelegateStub->didDispatchOn(callId, result, exception);
+}
+
+void WebDevToolsAgentImpl::dispatchOnInjectedScript(int callId, int injectedScriptId, const String& functionName, const String& jsonArgs, bool async)
+{
+ inspectorController()->inspectorBackend()->dispatchOnInjectedScript(
+ callId,
+ injectedScriptId,
+ functionName,
+ jsonArgs,
+ async);
+}
+
+void WebDevToolsAgentImpl::dispatchMessageFromFrontend(const WebDevToolsMessageData& data)
+{
+ if (ToolsAgentDispatch::dispatch(this, data))
+ return;
+
+ if (!m_attached)
+ return;
+
+ if (m_debuggerAgentImpl.get() && DebuggerAgentDispatch::dispatch(m_debuggerAgentImpl.get(), data))
+ return;
+}
+
+void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point)
+{
+ m_webViewImpl->inspectElementAt(point);
+}
+
+void WebDevToolsAgentImpl::setRuntimeFeatureEnabled(const WebString& feature, bool enabled)
+{
+ if (feature == kApuAgentFeatureName)
+ setApuAgentEnabled(enabled);
+ else if (feature == kTimelineFeatureName)
+ setTimelineProfilingEnabled(enabled);
+ else if (feature == kResourceTrackingFeatureName) {
+ InspectorController* ic = m_webViewImpl->page()->inspectorController();
+ if (enabled)
+ ic->enableResourceTracking(false /* not sticky */, false /* no reload */);
+ else
+ ic->disableResourceTracking(false /* not sticky */);
+ }
+}
+
+void WebDevToolsAgentImpl::sendRpcMessage(const WebDevToolsMessageData& data)
+{
+ m_client->sendMessageToFrontend(data);
+}
+
+void WebDevToolsAgentImpl::compileUtilityScripts()
+{
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(m_utilityContext);
+ // Inject javascript into the context.
+ WebCString injectedScriptJs = m_client->injectedScriptSource();
+ v8::Script::Compile(v8::String::New(
+ injectedScriptJs.data(),
+ injectedScriptJs.length()))->Run();
+ WebCString injectDispatchJs = m_client->injectedScriptDispatcherSource();
+ v8::Script::Compile(v8::String::New(
+ injectDispatchJs.data(),
+ injectDispatchJs.length()))->Run();
+}
+
+void WebDevToolsAgentImpl::initDevToolsAgentHost()
+{
+ BoundObject devtoolsAgentHost(m_utilityContext, this, "DevToolsAgentHost");
+ devtoolsAgentHost.addProtoFunction(
+ "dispatch",
+ WebDevToolsAgentImpl::jsDispatchOnClient);
+ devtoolsAgentHost.addProtoFunction(
+ "dispatchToApu",
+ WebDevToolsAgentImpl::jsDispatchToApu);
+ devtoolsAgentHost.addProtoFunction(
+ "evaluateOnSelf",
+ WebDevToolsAgentImpl::jsEvaluateOnSelf);
+ devtoolsAgentHost.addProtoFunction(
+ "runtimeFeatureStateChanged",
+ WebDevToolsAgentImpl::jsOnRuntimeFeatureStateChanged);
+ devtoolsAgentHost.build();
+
+ v8::HandleScope scope;
+ v8::Context::Scope utilityScope(m_utilityContext);
+ // Call custom code to create inspector backend wrapper in the utility context
+ // instead of calling V8DOMWrapper::convertToV8Object that would create the
+ // wrapper in the Page main frame context.
+ v8::Handle<v8::Object> backendWrapper = createInspectorBackendV8Wrapper();
+ if (backendWrapper.IsEmpty())
+ return;
+ m_utilityContext->Global()->Set(v8::String::New("InspectorBackend"), backendWrapper);
+}
+
+v8::Local<v8::Object> WebDevToolsAgentImpl::createInspectorBackendV8Wrapper()
+{
+ V8ClassIndex::V8WrapperType descriptorType = V8ClassIndex::INSPECTORBACKEND;
+ v8::Handle<v8::Function> function = V8InspectorBackend::GetTemplate()->GetFunction();
+ if (function.IsEmpty()) {
+ // Return if allocation failed.
+ return v8::Local<v8::Object>();
+ }
+ v8::Local<v8::Object> instance = SafeAllocation::newInstance(function);
+ if (instance.IsEmpty()) {
+ // Avoid setting the wrapper if allocation failed.
+ return v8::Local<v8::Object>();
+ }
+ InspectorBackend* backend = m_webViewImpl->page()->inspectorController()->inspectorBackend();
+ V8DOMWrapper::setDOMWrapper(instance, V8ClassIndex::ToInt(descriptorType), backend);
+ // Create a weak reference to the v8 wrapper of InspectorBackend to deref
+ // InspectorBackend when the wrapper is garbage collected.
+ backend->ref();
+ v8::Persistent<v8::Object> weakHandle = v8::Persistent<v8::Object>::New(instance);
+ weakHandle.MakeWeak(backend, &InspectorBackendWeakReferenceCallback);
+ return instance;
+}
+
+void WebDevToolsAgentImpl::resetInspectorFrontendProxy()
+{
+ disposeUtilityContext();
+ m_debuggerAgentImpl->createUtilityContext(m_webViewImpl->page()->mainFrame(), &m_utilityContext);
+ compileUtilityScripts();
+ initDevToolsAgentHost();
+
+ v8::HandleScope scope;
+ v8::Context::Scope contextScope(m_utilityContext);
+ ScriptState* state = ScriptState::forContext(
+ v8::Local<v8::Context>::New(m_utilityContext));
+ InspectorController* ic = inspectorController();
+ ic->setFrontendProxyObject(state, ScriptObject(state, m_utilityContext->Global()));
+}
+
+void WebDevToolsAgentImpl::setApuAgentEnabled(bool enabled)
+{
+ m_apuAgentEnabled = enabled;
+ SetApuAgentEnabledInUtilityContext(m_utilityContext, enabled);
+ InspectorController* ic = m_webViewImpl->page()->inspectorController();
+ if (enabled) {
+ m_resourceTrackingWasEnabled = ic->resourceTrackingEnabled();
+ ic->startTimelineProfiler();
+ if (!m_resourceTrackingWasEnabled) {
+ // TODO(knorton): Introduce some kind of agents dependency here so that
+ // user could turn off resource tracking while apu agent is on.
+ ic->enableResourceTracking(false, false);
+ }
+ m_debuggerAgentImpl->setAutoContinueOnException(true);
+ } else {
+ ic->stopTimelineProfiler();
+ if (!m_resourceTrackingWasEnabled)
+ ic->disableResourceTracking(false);
+ m_resourceTrackingWasEnabled = false;
+ }
+ m_client->runtimeFeatureStateChanged(
+ kApuAgentFeatureName,
+ enabled);
+}
+
+// static
+v8::Handle<v8::Value> WebDevToolsAgentImpl::jsDispatchOnClient(const v8::Arguments& args)
+{
+ v8::TryCatch exceptionCatcher;
+ String message = WebCore::toWebCoreStringWithNullCheck(args[0]);
+ if (message.isEmpty() || exceptionCatcher.HasCaught())
+ return v8::Undefined();
+ WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(v8::External::Cast(*args.Data())->Value());
+ agent->m_toolsAgentDelegateStub->dispatchOnClient(message);
+ return v8::Undefined();
+}
+
+// static
+v8::Handle<v8::Value> WebDevToolsAgentImpl::jsDispatchToApu(const v8::Arguments& args)
+{
+ v8::TryCatch exceptionCatcher;
+ String message = WebCore::toWebCoreStringWithNullCheck(args[0]);
+ if (message.isEmpty() || exceptionCatcher.HasCaught())
+ return v8::Undefined();
+ WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(
+ v8::External::Cast(*args.Data())->Value());
+ agent->m_apuAgentDelegateStub->dispatchToApu(message);
+ return v8::Undefined();
+}
+
+// static
+v8::Handle<v8::Value> WebDevToolsAgentImpl::jsEvaluateOnSelf(const v8::Arguments& args)
+{
+ String code;
+ {
+ v8::TryCatch exceptionCatcher;
+ code = WebCore::toWebCoreStringWithNullCheck(args[0]);
+ if (code.isEmpty() || exceptionCatcher.HasCaught())
+ return v8::Undefined();
+ }
+ WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(v8::External::Cast(*args.Data())->Value());
+ v8::Context::Scope(agent->m_utilityContext);
+ V8Proxy* proxy = V8Proxy::retrieve(agent->m_webViewImpl->page()->mainFrame());
+ v8::Local<v8::Value> result = proxy->runScript(v8::Script::Compile(v8::String::New(code.utf8().data())), true);
+ return result;
+}
+
+// static
+v8::Handle<v8::Value> WebDevToolsAgentImpl::jsOnRuntimeFeatureStateChanged(const v8::Arguments& args)
+{
+ v8::TryCatch exceptionCatcher;
+ String feature = WebCore::toWebCoreStringWithNullCheck(args[0]);
+ bool enabled = args[1]->ToBoolean()->Value();
+ if (feature.isEmpty() || exceptionCatcher.HasCaught())
+ return v8::Undefined();
+ WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(v8::External::Cast(*args.Data())->Value());
+ agent->m_client->runtimeFeatureStateChanged(feature, enabled);
+ return v8::Undefined();
+}
+
+
+WebCore::InspectorController* WebDevToolsAgentImpl::inspectorController()
+{
+ if (Page* page = m_webViewImpl->page())
+ return page->inspectorController();
+ return 0;
+}
+
+
+//------- plugin resource load notifications ---------------
+void WebDevToolsAgentImpl::identifierForInitialRequest(
+ unsigned long resourceId,
+ WebFrame* frame,
+ const WebURLRequest& request)
+{
+ if (InspectorController* ic = inspectorController()) {
+ WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(frame);
+ FrameLoader* frameLoader = webFrameImpl->frame()->loader();
+ DocumentLoader* loader = frameLoader->activeDocumentLoader();
+ ic->identifierForInitialRequest(resourceId, loader, request.toResourceRequest());
+ }
+}
+
+void WebDevToolsAgentImpl::willSendRequest(unsigned long resourceId, const WebURLRequest& request)
+{
+ if (InspectorController* ic = inspectorController())
+ ic->willSendRequest(resourceId, request.toResourceRequest(), ResourceResponse());
+}
+
+void WebDevToolsAgentImpl::didReceiveData(unsigned long resourceId, int length)
+{
+ if (InspectorController* ic = inspectorController())
+ ic->didReceiveContentLength(resourceId, length);
+}
+
+void WebDevToolsAgentImpl::didReceiveResponse(unsigned long resourceId, const WebURLResponse& response)
+{
+ if (InspectorController* ic = inspectorController())
+ ic->didReceiveResponse(resourceId, response.toResourceResponse());
+}
+
+void WebDevToolsAgentImpl::didFinishLoading(unsigned long resourceId)
+{
+ if (InspectorController* ic = inspectorController())
+ ic->didFinishLoading(resourceId);
+}
+
+void WebDevToolsAgentImpl::didFailLoading(unsigned long resourceId, const WebURLError& error)
+{
+ ResourceError resourceError;
+ if (InspectorController* ic = inspectorController())
+ ic->didFailLoading(resourceId, resourceError);
+}
+
+void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script)
+{
+ InspectorController* ic = inspectorController();
+ ic->evaluateForTestInFrontend(callId, script);
+}
+
+void WebDevToolsAgentImpl::setTimelineProfilingEnabled(bool enabled)
+{
+ InspectorController* ic = inspectorController();
+ if (enabled)
+ ic->startTimelineProfiler();
+ else
+ ic->stopTimelineProfiler();
+}
+
+WebDevToolsAgent* WebDevToolsAgent::create(WebView* webview, WebDevToolsAgentClient* client)
+{
+ return new WebDevToolsAgentImpl(static_cast<WebViewImpl*>(webview), client);
+}
+
+void WebDevToolsAgent::executeDebuggerCommand(const WebString& command, int callerId)
+{
+ DebuggerAgentManager::executeDebuggerCommand(command, callerId);
+}
+
+void WebDevToolsAgent::debuggerPauseScript()
+{
+ DebuggerAgentManager::pauseScript();
+}
+
+void WebDevToolsAgent::setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler)
+{
+ DebuggerAgentManager::setMessageLoopDispatchHandler(handler);
+}
+
+bool WebDevToolsAgent::dispatchMessageFromFrontendOnIOThread(const WebDevToolsMessageData& data)
+{
+ IORPCDelegate transport;
+ ProfilerAgentDelegateStub stub(&transport);
+ ProfilerAgentImpl agent(&stub);
+ return ProfilerAgentDispatch::dispatch(&agent, data);
+}
+
+} // namespace WebKit