Commit f6a2f614 authored by pfeldman@chromium.org's avatar pfeldman@chromium.org

DevTools: Implement styles toggle:

1. Split inject.js into injected object and the dispatch.
2. Surround utility function call with try/catch, pass potential exception all way to the client.
3. Fix user and user agent scripts detection
4. Introduce toggleStyle
5. Remove __defineGetter__ from the styles code: make things simple
6. Beautify code.

Review URL: http://codereview.chromium.org/99184

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14839 0039d316-1c4b-4281-b951-d872f2087c98
parent 251d1dbb
......@@ -17,6 +17,7 @@
#include "grit/webkit_resources.h"
#include "V8Binding.h"
#include "V8DOMWindow.h"
#include "v8_binding.h"
#include "v8_index.h"
#include "v8_proxy.h"
#include "webkit/glue/devtools/debugger_agent_impl.h"
......@@ -111,32 +112,44 @@ void DebuggerAgentImpl::SetDocument(Document* document) {
v8::Script::Compile(v8::String::New(jsonjs.as_string().c_str()))->Run();
StringPiece injectjs = webkit_glue::GetDataResource(IDR_DEVTOOLS_INJECT_JS);
v8::Script::Compile(v8::String::New(injectjs.as_string().c_str()))->Run();
StringPiece inject_dispatchjs = webkit_glue::GetDataResource(
IDR_DEVTOOLS_INJECT_DISPATCH_JS);
v8::Script::Compile(v8::String::New(
inject_dispatchjs.as_string().c_str()))->Run();
}
String DebuggerAgentImpl::ExecuteUtilityFunction(
const String &function_name,
Node* node,
const String& json_args) {
const String& json_args,
String* exception) {
v8::HandleScope scope;
ASSERT(!context_.IsEmpty());
v8::Context::Scope context_scope(context_);
v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(
context_->Global()->Get(v8::String::New(function_name.utf8().data())));
context_->Global()->Get(v8::String::New("devtools$$dispatch")));
v8::Handle<v8::Value> node_wrapper =
V8Proxy::ToV8Object(V8ClassIndex::NODE, node);
v8::Handle<v8::String> function_name_wrapper = v8::Handle<v8::String>(
v8::String::New(function_name.utf8().data()));
v8::Handle<v8::String> json_args_wrapper = v8::Handle<v8::String>(
v8::String::New(json_args.utf8().data()));
v8::Handle<v8::Value> args[] = {
function_name_wrapper,
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);
v8::TryCatch try_catch;
v8::Handle<v8::Value> res_obj = function->Call(context_->Global(), 3, args);
if (try_catch.HasCaught()) {
*exception = WebCore::ToWebCoreString(try_catch.Message()->Get());
return "";
} else {
v8::Handle<v8::String> res_json = v8::Handle<v8::String>::Cast(res_obj);
return WebCore::toWebCoreString(res_json);
}
}
void DebuggerAgentImpl::RunWithDeferredMessages(
......
......@@ -42,7 +42,8 @@ class DebuggerAgentImpl : public DebuggerAgent {
WebCore::String ExecuteUtilityFunction(
const WebCore::String& function_name,
WebCore::Node* node,
const WebCore::String& json_args);
const WebCore::String& json_args,
WebCore::String* exception);
static void RunWithDeferredMessages(
const HashSet<DebuggerAgentImpl*>& agents,
......
......@@ -549,7 +549,7 @@ WebInspector.ScopeChainSidebarPane.TreeElement.prototype.onpopulate =
/**
* Callback function used with the resolveChildren.
*/
WebInspector.ScopeChainSidebarPane.TreeElement.prototype.didResolveChildren_ =
WebInspector.ScopeChainSidebarPane.TreeElement.prototype.didResolveChildren_ =
function(object) {
this.removeChildren();
......@@ -558,3 +558,18 @@ WebInspector.ScopeChainSidebarPane.TreeElement.prototype.onpopulate =
this.appendChild(new constructor(object, name));
}
};
/**
* @override
*/
WebInspector.StylePropertyTreeElement.prototype.toggleEnabled =
function(event) {
var disabled = !event.target.checked;
var self = this;
devtools.tools.getDomAgent().toggleNodeStyleAsync(this.style, !disabled,
this.name,
function() {
WebInspector.panels.elements.updateStyles(true);
});
};
......@@ -158,7 +158,7 @@ RemoteToolsAgentStub.prototype.ExecuteUtilityFunction = function(callId,
functionName, nodeId, args) {
setTimeout(function() {
var result = [];
if (functionName == 'devtools$$getProperties') {
if (functionName == 'getProperties') {
result = [
'undefined', 'undefined_key', undefined,
'string', 'string_key', 'value',
......@@ -168,28 +168,30 @@ RemoteToolsAgentStub.prototype.ExecuteUtilityFunction = function(callId,
'boolean', 'boolean_key', true,
'number', 'num_key', 911,
'date', 'date_key', new Date() ];
} else if (functionName == 'devtools$$getPrototypes') {
} else if (functionName == 'getPrototypes') {
result = ['Proto1', 'Proto2', 'Proto3'];
} else if (functionName == 'devtools$$getStyles') {
} else if (functionName == 'getStyles') {
result = {
'computedStyle' : [['display', false, false, '', 'none']],
'inlineStyle' : [['display', false, false, '', 'none']],
'computedStyle' : [0, null, null, null, ['display', false, false, '', 'none']],
'inlineStyle' : [1, null, null, null, ['display', false, false, '', 'none']],
'styleAttributes' : {
attr: [['display', false, false, '', 'none']]
attr: [2, null, null, null, ['display', false, false, '', 'none']]
},
'matchedCSSRules' : [
{ 'selector' : 'S',
'style' : [['display', false, false, '', 'none']],
'parentStyleSheetHref' : 'http://localhost',
'parentStyleSheetOwnerNodeName' : 'DIV'
'style' : [3, null, null, null, ['display', false, false, '', 'none']],
'parentStyleSheet' : { 'href' : 'http://localhost',
'ownerNodeName' : 'DIV' }
}
]
};
} else if (functionName == 'toggleNodeStyle') {
alert('toggleNodeStyle ' + args);
} else {
alert('Unexpected utility function:' + functionName);
}
RemoteToolsAgent.DidExecuteUtilityFunction(callId,
goog.json.serialize(result));
goog.json.serialize(result), '');
}, 0);
};
......
......@@ -56,7 +56,6 @@ devtools.DomNode = function(doc, payload) {
this.firstChild = null;
this.parentNode = null;
this.styles_ = null;
this.disabledStyleProperties_ = {};
if (payload.length > devtools.PayloadIndex.CHILD_NODES) {
......@@ -64,6 +63,10 @@ devtools.DomNode = function(doc, payload) {
this.setChildrenPayload_(
payload[devtools.PayloadIndex.CHILD_NODES]);
}
this.computedStyle_ = null;
this.style = null;
this.matchedCSSRules_ = [];
};
......@@ -222,13 +225,9 @@ devtools.DomNode.prototype.setAttribute = function(name, value) {
*/
devtools.DomNode.prototype.addAttribute_ = function(name, value) {
var attr = {
'name': name,
'value': value,
node_: this,
/* Must be called after node.setStyles_. */
get style() {
return this.node_.styles_.attributes[this.name];
}
'name': name,
'value': value,
node_: this
};
this.attributesMap_[name] = attr;
......@@ -254,15 +253,6 @@ devtools.DomNode.prototype.removeAttribute = function(name) {
};
/**
* Returns inline style (if styles has loaded). Must be called after
* node.setStyles_.
*/
devtools.DomNode.prototype.__defineGetter__('style', function() {
return this.styles_.inlineStyle;
});
/**
* Makes available the following methods and properties:
* - node.style property
......@@ -281,40 +271,37 @@ devtools.DomNode.prototype.__defineGetter__('style', function() {
*/
devtools.DomNode.prototype.setStyles = function(computedStyle, inlineStyle,
styleAttributes, matchedCSSRules) {
var styles = {};
styles.computedStyle = this.makeStyle_(computedStyle);
styles.inlineStyle = this.makeStyle_(inlineStyle);
this.computedStyle_ = this.makeStyle_(computedStyle);
this.style = this.makeStyle_(inlineStyle);
styles.attributes = {};
for (var name in styleAttributes) {
var style = this.makeStyle_(styleAttributes[name]);
styles.attributes[name] = style;
if (this.attributesMap_[name]) {
this.attributesMap_[name].style =
this.makeStyle_(styleAttributes[name]);
}
}
styles.matchedCSSRules = [];
this.matchedCSSRules_ = [];
for (var i = 0; i < matchedCSSRules.length; i++) {
var descr = matchedCSSRules[i];
var selector = descr.selector;
var style = this.makeStyle_(descr.style);
var parentStyleSheet = undefined;
if (descr.parentStyleSheetHref) {
parentStyleSheet = {href: descr.parentStyleSheetHref};
if (descr.parentStyleSheetOwnerNodeName) {
parentStyleSheet.ownerNode =
{nodeName: descr.parentStyleSheetOwnerNodeName};
var rule = {};
rule.selectorText = descr['selector'];
rule.style = this.makeStyle_(descr['style']);
if (descr['parentStyleSheet']) {
var parentStyleMock = {};
parentStyleMock.href = descr['parentStyleSheet']['href'];
var nodeName = descr['parentStyleSheet']['ownerNodeName'];
if (nodeName) {
parentStyleMock.ownerNode = {
'nodeName': nodeName
};
}
rule.parentStyleSheet = parentStyleMock;
}
styles.matchedCSSRules.push({
'selectorText': selector,
'style': style,
'parentStyleSheet': parentStyleSheet
});
this.matchedCSSRules_.push(rule);
}
this.styles_ = styles;
};
......@@ -325,17 +312,24 @@ devtools.DomNode.prototype.setStyles = function(computedStyle, inlineStyle,
* @see devtools.CSSStyleDeclaration
*/
devtools.DomNode.prototype.makeStyle_ = function(payload) {
return payload && new devtools.CSSStyleDeclaration(payload);
var style = new devtools.CSSStyleDeclaration(payload);
style.nodeId_ = this.id_;
return style;
};
/**
* Remove references to the style information to release
* resources when styles are not going to be used.
* @see setStyles_.
* @see setStyles.
*/
devtools.DomNode.prototype.clearStyles = function() {
this.styles_ = null;
this.computedStyle = null;
this.style = null;
for (var name in this.attributesMap_) {
this.attributesMap_[name].style = null;
}
this.matchedCSSRules_ = null;
};
......@@ -450,19 +444,17 @@ devtools.DomWindow.prototype.Object = function() {
/**
* Simulates the DOM interface for styles. Must be called after
* node.setStyles_.
* Simulates the DOM interface for styles.
* @param {devtools.DomNode} node
* @return {CSSStyleDescription}
*/
devtools.DomWindow.prototype.getComputedStyle = function(node) {
return node.styles_.computedStyle;
return node.computedStyle_;
};
/**
* Simulates the DOM interface for styles. Must be called after
* node.setStyles_.
* Simulates the DOM interface for styles.
* @param {devtools.DomNode} nodeStyles
* @param {string} pseudoElement assumed to be empty string.
* @param {boolean} authorOnly assumed to be equal to authorOnly argument of
......@@ -471,7 +463,7 @@ devtools.DomWindow.prototype.getComputedStyle = function(node) {
*/
devtools.DomWindow.prototype.getMatchedCSSRules = function(node,
pseudoElement, authorOnly) {
return node.styles_.matchedCSSRules;
return node.matchedCSSRules_;
};
......@@ -837,9 +829,11 @@ devtools.DomAgent.prototype.getSearchResultNode = function(index) {
*/
devtools.DomAgent.prototype.getNodePropertiesAsync = function(nodeId,
path, protoDepth, callback) {
var callbackId = devtools.Callback.wrap(callback);
var mycallback =
goog.bind(this.utilityFunctionCallbackWrapper_, this, callback);
var callbackId = devtools.Callback.wrap(mycallback);
RemoteToolsAgent.ExecuteUtilityFunction(callbackId,
'devtools$$getProperties', nodeId,
'getProperties', nodeId,
goog.json.serialize([path, protoDepth]));
};
......@@ -851,9 +845,11 @@ devtools.DomAgent.prototype.getNodePropertiesAsync = function(nodeId,
*/
devtools.DomAgent.prototype.getNodePrototypesAsync = function(nodeId,
callback) {
var callbackId = devtools.Callback.wrap(callback);
var mycallback =
goog.bind(this.utilityFunctionCallbackWrapper_, this, callback);
var callbackId = devtools.Callback.wrap(mycallback);
RemoteToolsAgent.ExecuteUtilityFunction(callbackId,
'devtools$$getPrototypes', nodeId, '');
'getPrototypes', nodeId, '[]');
};
......@@ -865,37 +861,76 @@ devtools.DomAgent.prototype.getNodePrototypesAsync = function(nodeId,
*/
devtools.DomAgent.prototype.getNodeStylesAsync = function(node,
authorOnly, callback) {
var callbackId = devtools.Callback.wrap(callback);
var mycallback =
goog.bind(this.utilityFunctionCallbackWrapper_, this, callback);
var callbackId = devtools.Callback.wrap(mycallback);
RemoteToolsAgent.ExecuteUtilityFunction(callbackId,
'devtools$$getStyles',
'getStyles',
node.id_,
goog.json.serialize(authorOnly));
goog.json.serialize([authorOnly]));
};
/**
* Toggles style with given id on/off.
* @param {devtools.CSSStyleDeclaration} style Style to toggle.
* @param {boolean} enabled True if style should be enabled.
* @param {string} name Style name.
* @param {Function} callback.
*/
devtools.DomAgent.prototype.toggleNodeStyleAsync = function(
style, enabled, name, callback) {
var mycallback =
goog.bind(this.utilityFunctionCallbackWrapper_, this, callback);
var callbackId = devtools.Callback.wrap(mycallback);
RemoteToolsAgent.ExecuteUtilityFunction(callbackId,
'toggleNodeStyle',
style.nodeId_,
goog.json.serialize([style.id_, enabled, name]));
};
/**
* Dumps exception if something went wrong in ExecuteUtilityFunction.
*/
devtools.DomAgent.prototype.utilityFunctionCallbackWrapper_ =
function(callback, result, exception) {
if (exception && exception.length) {
debugPrint('Exception in ExecuteUtilityFunction styles:' + exception);
return;
}
callback(result);
};
/**
* Represents remote CSSStyleDeclaration for using in StyleSidebarPane.
* @param {Array<Object>} payload built by devtools$$getStyle from the injected
* js.
* @consctuctor
* @param {id, Array<Object>} payload built by inject's getStyle from the
* injected js.
* @constructor
*/
devtools.CSSStyleDeclaration = function(payload) {
this.length = payload.length;
this.id_ = payload[0];
this.__disabledProperties = payload[1];
this.__disabledPropertyValues = payload[2];
this.__disabledPropertyPriorities = payload[3];
this.length = payload.length - 4;
this.priority_ = {};
this.implicit_ = {};
this.shorthand_ = {};
this.value_ = {};
for (var i = 0; i < payload.length; ++i) {
for (var i = 4; i < payload.length; ++i) {
var p = payload[i];
var name = p[0];
this.priority_[name] = p[1];
this.implicit_[name] = p[2];
this.shorthand_[name] = p[3];
this.value_[name] = p[4];
this[i] = name;
this[i - 4] = name;
}
};
......
This diff is collapsed.
// 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 Injects 'injected' object into the inspectable page.
*/
/**
* Dispatches host calls into the injected function calls.
*/
goog.require('devtools.Injected');
/**
* Injected singleton.
*/
var devtools$$obj = new devtools.Injected();
/**
* Main dispatch method, all calls from the host go through this one.
* @param {string} functionName Function to call
* @param {Node} node Node context of the call.
* @param {string} json_args JSON-serialized call parameters.
* @return {string} JSON-serialized result of the dispatched call.
*/
function devtools$$dispatch(functionName, node, json_args) {
var params = goog.json.parse(json_args);
params.splice(0, 0, node);
var result = devtools$$obj[functionName].apply(devtools$$obj, params);
return goog.json.serialize(result);
};
......@@ -41,7 +41,8 @@ DEFINE_RPC_CLASS(ToolsAgent, TOOLS_AGENT_STRUCT)
METHOD2(FrameNavigate, std::string /* url */, bool /* top_level */) \
\
/* Response to the GetNodeProperties. */ \
METHOD2(DidExecuteUtilityFunction, int /* call_id */, String /* json */) \
METHOD3(DidExecuteUtilityFunction, int /* call_id */, String /* result */, \
String /* exception */) \
\
/* Adds message to console. */ \
METHOD1(AddMessageToConsole, Value /* message */)
......
......@@ -195,12 +195,13 @@ void WebDevToolsAgentImpl::ExecuteUtilityFunction(
const String& json_args) {
Node* node = dom_agent_impl_->GetNodeForId(node_id);
String result;
String exception;
if (node) {
result = debugger_agent_impl_->ExecuteUtilityFunction(function_name, node,
json_args);
json_args, &exception);
}
tools_agent_delegate_stub_->DidExecuteUtilityFunction(call_id,
result);
result, exception);
}
void WebDevToolsAgentImpl::ClearConsoleMessages() {
......@@ -271,4 +272,3 @@ void WebDevToolsAgent::SetMessageLoopDispatchHandler(
MessageLoopDispatchHandler handler) {
DebuggerAgentManager::SetMessageLoopDispatchHandler(handler);
}
......@@ -20,6 +20,7 @@
<include name="IDR_TEXTAREA_RESIZER" file="resources\textarea_resize_corner.png" type="BINDATA" />
<include name="IDR_DEVTOOLS_BASE_JS" file="devtools\js\base.js" type="BINDATA" />
<include name="IDR_DEVTOOLS_JSON_JS" file="devtools\js\json.js" type="BINDATA" />
<include name="IDR_DEVTOOLS_INJECT_DISPATCH_JS" file="devtools\js\inject_dispatch.js" type="BINDATA" />
<include name="IDR_DEVTOOLS_INJECT_JS" file="devtools\js\inject.js" type="BINDATA" />
<if expr="os == 'linux2'">
<include name="IDR_LINUX_CHECKBOX_OFF" file="resources\linux-checkbox-off.png" type="BINDATA" />
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment