diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-01 10:04:26 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-01 10:04:26 +0000 |
commit | c85fd527a167276e470a9cc414fae0343072a594 (patch) | |
tree | d3388392feebdc7e117f0fcdcb434c959b9e4d15 /webkit/glue/devtools | |
parent | 43cf325be09379594dd96e0b35146585cdfd6a34 (diff) | |
download | chromium_src-c85fd527a167276e470a9cc414fae0343072a594.zip chromium_src-c85fd527a167276e470a9cc414fae0343072a594.tar.gz chromium_src-c85fd527a167276e470a9cc414fae0343072a594.tar.bz2 |
DevTools: Support r/o properties inspection.
Review URL: http://codereview.chromium.org/56067
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12949 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue/devtools')
-rw-r--r-- | webkit/glue/devtools/debugger_agent_impl.cc | 62 | ||||
-rw-r--r-- | webkit/glue/devtools/debugger_agent_impl.h | 20 | ||||
-rw-r--r-- | webkit/glue/devtools/dom_agent_impl.cc | 20 | ||||
-rw-r--r-- | webkit/glue/devtools/dom_agent_impl.h | 2 | ||||
-rw-r--r-- | webkit/glue/devtools/js/base.js | 2 | ||||
-rw-r--r-- | webkit/glue/devtools/js/debugger_agent.js | 2 | ||||
-rw-r--r-- | webkit/glue/devtools/js/devtools.html | 2 | ||||
-rw-r--r-- | webkit/glue/devtools/js/devtools.js | 145 | ||||
-rw-r--r-- | webkit/glue/devtools/js/devtools_host_stub.js | 37 | ||||
-rw-r--r-- | webkit/glue/devtools/js/inject.js | 82 | ||||
-rw-r--r-- | webkit/glue/devtools/tools_agent.h | 16 |
11 files changed, 363 insertions, 27 deletions
diff --git a/webkit/glue/devtools/debugger_agent_impl.cc b/webkit/glue/devtools/debugger_agent_impl.cc index fde98cb..b52db2a 100644 --- a/webkit/glue/devtools/debugger_agent_impl.cc +++ b/webkit/glue/devtools/debugger_agent_impl.cc @@ -4,11 +4,24 @@ #include "config.h" -#include <wtf/Assertions.h> +#include "Document.h" +#include "Node.h" #undef LOG +#include "grit/webkit_resources.h" +#include "V8Binding.h" +#include "v8_index.h" +#include "v8_proxy.h" #include "webkit/glue/devtools/debugger_agent_impl.h" #include "webkit/glue/devtools/debugger_agent_manager.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" + +using WebCore::Document; +using WebCore::Node; +using WebCore::String; +using WebCore::V8ClassIndex; +using WebCore::V8Proxy; DebuggerAgentImpl::DebuggerAgentImpl(DebuggerAgentDelegate* delegate) : delegate_(delegate) { @@ -26,3 +39,50 @@ void DebuggerAgentImpl::DebugBreak() { void DebuggerAgentImpl::DebuggerOutput(const std::string& command) { delegate_->DebuggerOutput(command); } + +void DebuggerAgentImpl::SetDocument(Document* document) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); + if (!document) { + context_ = v8::Context::New(NULL /* no extensions */, global_template); + return; + } + + // TODO(pfeldman): Do not modify existing context - introduce utility one + // instead. + context_ = v8::Persistent<v8::Context>::New( + V8Proxy::GetContext(document->frame())); + v8::Context::Scope context_scope(context_); + + std::string basejs = webkit_glue::GetDataResource(IDR_DEVTOOLS_BASE_JS); + v8::Script::Compile(v8::String::New(basejs.c_str()))->Run(); + std::string jsonjs = webkit_glue::GetDataResource(IDR_DEVTOOLS_JSON_JS); + v8::Script::Compile(v8::String::New(jsonjs.c_str()))->Run(); + std::string injectjs = webkit_glue::GetDataResource(IDR_DEVTOOLS_INJECT_JS); + v8::Script::Compile(v8::String::New(injectjs.c_str()))->Run(); +} + +String DebuggerAgentImpl::ExecuteUtilityFunction( + const String &function_name, + Node* node, + const String& json_args) { + v8::HandleScope scope; + v8::Context::Scope context_scope(v8::Local<v8::Context>::New(context_)); + v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast( + context_->Global()->Get(v8::String::New(function_name.utf8().data()))); + + v8::Handle<v8::Value> node_wrapper = + V8Proxy::ToV8Object(V8ClassIndex::NODE, node); + v8::Handle<v8::String> json_args_wrapper = v8::Handle<v8::String>( + v8::String::New(json_args.utf8().data())); + v8::Handle<v8::Value> args[] = { + node_wrapper, + json_args_wrapper + }; + + v8::Handle<v8::Value> res_obj = function->Call( + context_->Global(), 2, args); + + v8::Handle<v8::String> res_json = v8::Handle<v8::String>::Cast(res_obj); + return WebCore::toWebCoreString(res_json); +} diff --git a/webkit/glue/devtools/debugger_agent_impl.h b/webkit/glue/devtools/debugger_agent_impl.h index 2eb6411..aa0db70 100644 --- a/webkit/glue/devtools/debugger_agent_impl.h +++ b/webkit/glue/devtools/debugger_agent_impl.h @@ -5,20 +5,38 @@ #ifndef WEBKIT_GLUE_DEVTOOLS_DEBUGGER_AGENT_IMPL_H_ #define WEBKIT_GLUE_DEVTOOLS_DEBUGGER_AGENT_IMPL_H_ +#include "v8.h" #include "webkit/glue/devtools/debugger_agent.h" +namespace WebCore { +class Document; +class Node; +class String; +} + class DebuggerAgentImpl : public DebuggerAgent { public: explicit DebuggerAgentImpl(DebuggerAgentDelegate* delegate); virtual ~DebuggerAgentImpl(); + // Initializes dom agent with the given document. + void SetDocument(WebCore::Document* document); + // DebuggerAgent implementation. virtual void DebugBreak(); void DebuggerOutput(const std::string& out); - private: + // Executes utility function with the given node and json + // args as parameters. These functions must be implemented in + // the inject.js file. + WebCore::String ExecuteUtilityFunction( + const WebCore::String& function_name, + WebCore::Node* node, + const WebCore::String& json_args); + private: + v8::Persistent<v8::Context> context_; DebuggerAgentDelegate* delegate_; DISALLOW_COPY_AND_ASSIGN(DebuggerAgentImpl); diff --git a/webkit/glue/devtools/dom_agent_impl.cc b/webkit/glue/devtools/dom_agent_impl.cc index a3a3d75..81016462 100644 --- a/webkit/glue/devtools/dom_agent_impl.cc +++ b/webkit/glue/devtools/dom_agent_impl.cc @@ -478,24 +478,24 @@ ListValue* DomAgentImpl::BuildValueForNode(Node* node, int depth) { } ListValue* DomAgentImpl::BuildValueForElementAttributes(Element* element) { - OwnPtr<ListValue> attributesValue(new ListValue()); + OwnPtr<ListValue> attributes_value(new ListValue()); // Go through all attributes and serialize them. - const NamedNodeMap *attrMap = element->attributes(true); - if (!attrMap) { - return attributesValue.release(); + const NamedNodeMap* attr_map = element->attributes(true); + if (!attr_map) { + return attributes_value.release(); } - unsigned numAttrs = attrMap->length(); - for (unsigned i = 0; i < numAttrs; i++) { + unsigned num_attrs = attr_map->length(); + for (unsigned i = 0; i < num_attrs; i++) { // Add attribute pair - const Attribute *attribute = attrMap->attributeItem(i); + const Attribute *attribute = attr_map->attributeItem(i); OwnPtr<Value> name(Value::CreateStringValue( webkit_glue::StringToStdWString(attribute->name().toString()))); OwnPtr<Value> value(Value::CreateStringValue( webkit_glue::StringToStdWString(attribute->value()))); - attributesValue->Append(name.release()); - attributesValue->Append(value.release()); + attributes_value->Append(name.release()); + attributes_value->Append(value.release()); } - return attributesValue.release(); + return attributes_value.release(); } ListValue* DomAgentImpl::BuildValueForElementChildren( diff --git a/webkit/glue/devtools/dom_agent_impl.h b/webkit/glue/devtools/dom_agent_impl.h index 0a8eb31..89a9df9 100644 --- a/webkit/glue/devtools/dom_agent_impl.h +++ b/webkit/glue/devtools/dom_agent_impl.h @@ -77,7 +77,7 @@ class DomAgentImpl : public DomAgent { static PassRefPtr<EventListenerWrapper> Create( DomAgentImpl* dom_agent_impl); virtual ~EventListenerWrapper() {} - virtual void handleEvent(WebCore::Event* event, bool isWindowEvent); + virtual void handleEvent(WebCore::Event* event, bool is_window_event); private: explicit EventListenerWrapper(DomAgentImpl* dom_agent_impl); DomAgentImpl* dom_agent_impl_; diff --git a/webkit/glue/devtools/js/base.js b/webkit/glue/devtools/js/base.js index d0e35e3..307765c 100644 --- a/webkit/glue/devtools/js/base.js +++ b/webkit/glue/devtools/js/base.js @@ -33,7 +33,7 @@ * @define {boolean} Overridden to true by the compiler when * --mark_as_compiled is specified. */ -var COMPILED = false; +var COMPILED = true; /** diff --git a/webkit/glue/devtools/js/debugger_agent.js b/webkit/glue/devtools/js/debugger_agent.js index 820393c..b22ff20 100644 --- a/webkit/glue/devtools/js/debugger_agent.js +++ b/webkit/glue/devtools/js/debugger_agent.js @@ -50,7 +50,7 @@ devtools.DebuggerAgent.prototype.requestScripts = function() { }); devtools.DebuggerAgent.sendCommand_(cmd); // Force v8 execution so that it gets to processing the requested command. - devtools.tools.evaluateJavaSctipt('javascript:void(0)'); + devtools.tools.evaluateJavaScript("javascript:void(0)");
}; diff --git a/webkit/glue/devtools/js/devtools.html b/webkit/glue/devtools/js/devtools.html index 2e995d0..3176bea 100644 --- a/webkit/glue/devtools/js/devtools.html +++ b/webkit/glue/devtools/js/devtools.html @@ -10,7 +10,7 @@ example not to create WebKit Database tab and remove corresponding js files from here. Longer term we would like to employ closure + basic js compilation. That way js libraries would know their dependencies and js compiler would be able to compile them into a single file. After that this HTML file will -include single <sctipt src='fe-compiled.js'> and will become upstreamable. +include single <script src='fe-compiled.js'> and will become upstreamable. Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. diff --git a/webkit/glue/devtools/js/devtools.js b/webkit/glue/devtools/js/devtools.js index 31d7782..78f47da 100644 --- a/webkit/glue/devtools/js/devtools.js +++ b/webkit/glue/devtools/js/devtools.js @@ -14,7 +14,9 @@ goog.require('devtools.DomAgent'); goog.require('devtools.NetAgent'); devtools.ToolsAgent = function() { - RemoteToolsAgent.DidEvaluateJavaSctipt = devtools.Callback.processCallback; + RemoteToolsAgent.DidEvaluateJavaScript = devtools.Callback.processCallback; + RemoteToolsAgent.DidExecuteUtilityFunction = + devtools.Callback.processCallback; RemoteToolsAgent.UpdateFocusedNode = goog.bind(this.updateFocusedNode, this); RemoteToolsAgent.FrameNavigate = @@ -36,13 +38,44 @@ devtools.ToolsAgent.prototype.reset = function() { /** - * @param {string} script Sctipt exression to be evaluated in the context of the + * @param {string} script Script exression to be evaluated in the context of the * inspected page. - * @param {Function} callback + * @param {function(string):undefined} callback Function to call with the + * result. */ -devtools.ToolsAgent.prototype.evaluateJavaSctipt = function(script, callback) { +devtools.ToolsAgent.prototype.evaluateJavaScript = function(script, callback) { var callbackId = devtools.Callback.wrap(callback); - RemoteToolsAgent.EvaluateJavaSctipt(callbackId, script); + RemoteToolsAgent.EvaluateJavaScript(callbackId, script); +}; + + +/** + * Returns all properties of the given node. + * @param {devtools.DomNode} node Node to get properties for. + * @param {Array.<string>} path Path to the object. + * @param {number} protoDepth Depth to the exact proto level. + * @param {function(string):undefined} callback Function to call with the + * result. + */ +devtools.ToolsAgent.prototype.getNodePropertiesAsync = function(nodeId, + path, protoDepth, callback) { + var callbackId = devtools.Callback.wrap(callback); + RemoteToolsAgent.ExecuteUtilityFunction(callbackId, + 'devtools$$getProperties', nodeId, + goog.json.serialize([path, protoDepth])); +}; + + +/** + * Returns prototype chain for a given node. + * @param {devtools.DomNode} node Node to get prototypes for. + * @param {Function} callback. + */ +devtools.ToolsAgent.prototype.getNodePrototypesAsync = function(nodeId, + callback) { + var callbackId = devtools.Callback.wrap(callback); + RemoteToolsAgent.ExecuteUtilityFunction(callbackId, + 'devtools$$getPrototypes', nodeId, ''); }; @@ -184,3 +217,105 @@ WebInspector.ElementsPanel.prototype.jumpToPreviousSearchResult = function() { WebInspector.Console.prototype._evalInInspectedWindow = function(expr) { return devtools.tools.evaluate(expr); }; + + +WebInspector.PropertiesSidebarPane.prototype.update = function(object) { + var body = this.bodyElement; + body.removeChildren(); + + this.sections = []; + + if (!object) { + return; + } + + + var self = this; + devtools.tools.getNodePrototypesAsync(object.id_, function(json) { + var prototypes = goog.json.parse(json); + for (var i = 0; i < prototypes.length; ++i) { + var prototype = {}; + prototype.id_ = object.id_; + prototype.protoDepth_ = i; + var section = new WebInspector.ObjectPropertiesSection(prototype, + prototypes[i]); + self.sections.push(section); + body.appendChild(section.element); + } + }); +}; + + +WebInspector.ObjectPropertiesSection.prototype.onpopulate = function() { + var nodeId = this.object.id_; + var protoDepth = this.object.protoDepth_; + var path = []; + devtools.tools.getNodePropertiesAsync(nodeId, path, protoDepth, + goog.partial(WebInspector.didGetNodePropertiesAsync_, + this.propertiesTreeOutline, + this.treeElementConstructor, + nodeId, + path)); +}; + + +WebInspector.ObjectPropertyTreeElement.prototype.onpopulate = function() { + var nodeId = this.parentObject.devtools$$nodeId_; + var path = this.parentObject.devtools$$path_.slice(0); + path.push(this.propertyName); + devtools.tools.getNodePropertiesAsync(nodeId, path, -1, goog.partial( + WebInspector.didGetNodePropertiesAsync_, + this, + this.treeOutline.section.treeElementConstructor, + nodeId, path)); +}; + + +/** + * Dummy object used during properties inspection. + * @see WebInspector.didGetNodePropertiesAsync_ + */ +WebInspector.dummyObject_ = { 'foo' : 'bar' }; + + +/** + * Dummy function used during properties inspection. + * @see WebInspector.didGetNodePropertiesAsync_ + */ +WebInspector.dummyFunction_ = function() {}; + + +/** + * Callback function used with the getNodeProperties. + */ +WebInspector.didGetNodePropertiesAsync_ = function(treeOutline, constructor, + nodeId, path, json) { + var props = goog.json.parse(json); + var properties = []; + var obj = {}; + obj.devtools$$nodeId_ = nodeId; + obj.devtools$$path_ = path; + for (var i = 0; i < props.length; i += 3) { + var type = props[i]; + var name = props[i + 1]; + var value = props[i + 2]; + properties.push(name); + if (type == 'object') { + // fake object is going to be replaced on expand. + obj[name] = WebInspector.dummyObject_; + } else if (type == 'function') { + // fake function is going to be replaced on expand. + obj[name] = WebInspector.dummyFunction_; + } else { + obj[name] = value; + } + } + properties.sort(); + + treeOutline.removeChildren(); + + for (var i = 0; i < properties.length; ++i) { + var propertyName = properties[i]; + treeOutline.appendChild(new constructor(obj, propertyName)); + } +}; diff --git a/webkit/glue/devtools/js/devtools_host_stub.js b/webkit/glue/devtools/js/devtools_host_stub.js index 17b49e8..73b290e 100644 --- a/webkit/glue/devtools/js/devtools_host_stub.js +++ b/webkit/glue/devtools/js/devtools_host_stub.js @@ -146,10 +146,43 @@ RemoteToolsAgentStub.prototype.evaluate = function(expr) { window.eval(expr); }; -RemoteToolsAgentStub.prototype.EvaluateJavaSctipt = function(callId, script) { +RemoteToolsAgentStub.prototype.EvaluateJavaScript = function(callId, script) { setTimeout(function() { var result = eval(script); - RemoteToolsAgent.DidEvaluateJavaSctipt(callId, result); + RemoteToolsAgent.DidEvaluateJavaScript(callId, result); + }, 0); +}; + + +RemoteToolsAgentStub.prototype.ExecuteUtilityFunction = function(callId, + functionName, nodeId, args) { + setTimeout(function() { + var result = []; + if (functionName == 'devtools$$getProperties') { + result = [ + 'undefined', 'undefined_key', undefined, + 'string', 'string_key', 'value', + 'function', 'func', undefined, + 'array', 'array_key', [10], + 'object', 'object_key', undefined, + 'boolean', 'boolean_key', true, + 'number', 'num_key', 911, + 'date', 'date_key', new Date() ]; + } else if (functionName == 'devtools$$getPrototypes') { + result = ['Proto1', 'Proto2', 'Proto3']; + } else { + alert('Unexpected utility function:' + functionName); + } + RemoteToolsAgent.DidExecuteUtilityFunction(callId, + goog.json.serialize(result)); + }, 0); +}; + + +RemoteToolsAgentStub.prototype.GetNodePrototypes = function(callId, nodeId) { + setTimeout(function() { + RemoteToolsAgent.DidGetNodePrototypes(callId, + goog.json.serialize()); }, 0); }; diff --git a/webkit/glue/devtools/js/inject.js b/webkit/glue/devtools/js/inject.js new file mode 100644 index 0000000..4e97462 --- /dev/null +++ b/webkit/glue/devtools/js/inject.js @@ -0,0 +1,82 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Javascript that is being injected into the inspectable page
+ * while debugging.
+ */
+goog.require('goog.json');
+
+/**
+ * Returns JSON-serialized array of properties for a given node
+ * on a given path.
+ * @param {Node} node Node to get property value for.
+ * @param {string} args JSON-serialized [{Array.<string>} Path to the
+ * nested object, {number} Depth to the actual proto to inspect].
+ * @return {string} JSON-serialized array where each property is represented
+ * by the tree entryies [{string} type, {string} name, {Object} value].
+ */
+function devtools$$getProperties(node, args) {
+ // Parse parameters.
+ var parsedArgs = goog.json.parse(args);
+ var path = parsedArgs[0];
+ var protoDepth = parsedArgs[1];
+
+ var result = [];
+ var obj = node;
+
+ // Follow the path.
+ for (var i = 0; obj && i < path.length; ++i) {
+ obj = obj[path[i]];
+ }
+
+ if (!obj) {
+ return '[]';
+ }
+
+ // Get to the necessary proto layer.
+ for (var i = 0; obj && i < protoDepth; ++i) {
+ obj = obj.__proto__;
+ }
+
+ if (!obj) {
+ return '[]';
+ }
+
+ // Go over properties, prepare results.
+ for (var name in obj) {
+ if (protoDepth != -1 && 'hasOwnProperty' in obj &&
+ !obj.hasOwnProperty(name)) {
+ continue;
+ }
+ var type = typeof obj[name];
+ result.push(type);
+ result.push(name);
+ if (type == 'string') {
+ var str = obj[name];
+ result.push(str.length > 99 ? str.substr(0, 99) + '...' : str);
+ } else if (type != 'object' && type != 'array' &&
+ type != 'function') {
+ result.push(obj[name]);
+ } else {
+ result.push(undefined);
+ }
+ }
+ return goog.json.serialize(result);
+}
+
+
+/**
+ * Returns JSON-serialized array of prototypes for a given node.
+ * @param {Node} node Node to get prorotypes for.
+ * @return {string} JSON-serialized array where each item is a proto name.
+ */
+function devtools$$getPrototypes(node, args) {
+ var result = [];
+ for (var prototype = node; prototype; prototype = prototype.__proto__) {
+ var description = Object.prototype.toString.call(prototype);
+ result.push(description.replace(/^\[object (.*)\]$/i, '$1'));
+ }
+ return goog.json.serialize(result);
+}
diff --git a/webkit/glue/devtools/tools_agent.h b/webkit/glue/devtools/tools_agent.h index 8db0de6..fb9268c 100644 --- a/webkit/glue/devtools/tools_agent.h +++ b/webkit/glue/devtools/tools_agent.h @@ -17,7 +17,12 @@ METHOD0(HideDOMNodeHighlight) \ \ /* Executes JavaScript in the context of the inspected window. */ \ - METHOD2(EvaluateJavaSctipt, int /* call_id */, String /* JS expression */) + METHOD2(EvaluateJavaScript, int /* call_id */, String /* JS expression */) \ + \ + /* Requests that utility js function is executed with the given args. */ \ + METHOD4(ExecuteUtilityFunction, int /* call_id */, \ + String /* function_name */, int /* context_node_id */, \ + String /* json_args */) DEFINE_RPC_CLASS(ToolsAgent, TOOLS_AGENT_STRUCT) @@ -26,11 +31,14 @@ DEFINE_RPC_CLASS(ToolsAgent, TOOLS_AGENT_STRUCT) /* Updates focused node on the client. */ \ METHOD1(UpdateFocusedNode, int /* node_id */) \ \ - /* Response message to EvaluateJavaSctipt. */ \ - METHOD2(DidEvaluateJavaSctipt, int /* call_id */, String /* result */) \ + /* Response message to EvaluateJavaScript. */ \ + METHOD2(DidEvaluateJavaScript, int /* call_id */, String /* result */) \ \ /* Updates focused node on the client. */ \ - METHOD2(FrameNavigate, std::string /* url */, bool /* top_level */) + METHOD2(FrameNavigate, std::string /* url */, bool /* top_level */) \ + \ + /* Response to the GetNodeProperties. */ \ + METHOD2(DidExecuteUtilityFunction, int /* call_id */, String /* json */) DEFINE_RPC_CLASS(ToolsAgentDelegate, TOOLS_AGENT_DELEGATE_STRUCT) |