diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 13:59:13 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 13:59:13 +0000 |
commit | cad26edcdccf0cc36afab1e8584ff165460373e0 (patch) | |
tree | 16bdfeccca051afc0212cbbb70f35aa49b9fc9c8 /webkit | |
parent | 51996efd0e452dc7cea81616783958cb3fbf81e2 (diff) | |
download | chromium_src-cad26edcdccf0cc36afab1e8584ff165460373e0.zip chromium_src-cad26edcdccf0cc36afab1e8584ff165460373e0.tar.gz chromium_src-cad26edcdccf0cc36afab1e8584ff165460373e0.tar.bz2 |
DevTools: Add support for r/o CSS inspection. Original review CL by serya : http://codereview.chromium.org/57007
Review URL: http://codereview.chromium.org/60047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13009 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/glue/devtools/dom_agent.h | 7 | ||||
-rw-r--r-- | webkit/glue/devtools/dom_agent_impl.cc | 97 | ||||
-rw-r--r-- | webkit/glue/devtools/dom_agent_impl.h | 10 | ||||
-rw-r--r-- | webkit/glue/devtools/js/devtools_host_stub.js | 15 | ||||
-rw-r--r-- | webkit/glue/devtools/js/dom_agent.js | 272 | ||||
-rw-r--r-- | webkit/glue/devtools/js/inspector_controller_impl.js | 20 |
6 files changed, 377 insertions, 44 deletions
diff --git a/webkit/glue/devtools/dom_agent.h b/webkit/glue/devtools/dom_agent.h index 205e4d9..690a194 100644 --- a/webkit/glue/devtools/dom_agent.h +++ b/webkit/glue/devtools/dom_agent.h @@ -33,6 +33,10 @@ /* Perform search. */ \ METHOD2(PerformSearch, int /* call_id */, String /* query */) \ \ + /* Requests that the element's style information is sent to the client. */ \ + METHOD3(GetNodeStyles, int /* call_id */, int /* id */,\ + bool /* author_only */) \ + \ /* Tells dom agent that the client has lost all of the dom-related information and is no longer interested in the notifications related to the nodes issued earlier. */ \ @@ -48,6 +52,9 @@ DEFINE_RPC_CLASS(DomAgent, DOM_AGENT_STRUCT) /* Perform search. */ \ METHOD2(DidPerformSearch, int /* call_id */, Value /* results */) \ \ + /* Response to GetNodeStyles. */ \ + METHOD2(DidGetNodeStyles, int /* call_id */, Value /* styles */) \ + \ /* Ack for the Set/RemoveAttribute, SetTextNodeValue. */ \ METHOD2(DidApplyDomChange, int /* call_id */, bool /* success */) \ \ diff --git a/webkit/glue/devtools/dom_agent_impl.cc b/webkit/glue/devtools/dom_agent_impl.cc index 81016462..0e83054 100644 --- a/webkit/glue/devtools/dom_agent_impl.cc +++ b/webkit/glue/devtools/dom_agent_impl.cc @@ -5,6 +5,12 @@ #include "config.h" #include "AtomicString.h" +#include "CSSComputedStyleDeclaration.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSStyleRule.h" +#include "CSSStyleSheet.h" +#include "DOMWindow.h" #include "Document.h" #include "Event.h" #include "EventListener.h" @@ -435,6 +441,95 @@ void DomAgentImpl::PerformSearch(int call_id, const String& query) { delegate_->DidPerformSearch(call_id, list); } +void DomAgentImpl::GetNodeStyles(int call_id, + int element_id, + bool author_only) { + // TODO (serya): Currently styles are serialized as cssText. + // It could be not enough. + String computed_style; + String inline_style; + OwnPtr<DictionaryValue> style_attributes(new DictionaryValue()); + OwnPtr<ListValue> matched_css_rules(new ListValue()); + + Node* node = GetNodeForId(element_id); + DCHECK(!node || node->nodeType() == Node::ELEMENT_NODE); + if (node && node->nodeType() == Node::ELEMENT_NODE) { + Element* element = static_cast<Element*>(node); + inline_style = element->style()->cssText(); + DOMWindow* window = element->document()->defaultView(); + computed_style = window->getComputedStyle(element, "")->cssText(); + BuildValueForCSSRules(*window->getMatchedCSSRules(element, "", + author_only), *matched_css_rules); + BuildValueForAttributeStyles(*element->attributes(), + *style_attributes); + } + + DictionaryValue styles; + + styles.SetString(L"computedStyle", + webkit_glue::StringToStdString(computed_style)); + styles.SetString(L"inlineStyle", + webkit_glue::StringToStdString(inline_style)); + styles.Set(L"styleAttributes", style_attributes.release()); + styles.Set(L"matchedCSSRules", matched_css_rules.release()); + + delegate_->DidGetNodeStyles(call_id, styles); +} + +void DomAgentImpl::BuildValueForCSSRules(CSSRuleList& matched, + ListValue& descriptionList) { + for (unsigned i = 0; i < matched.length(); ++i) { + if (!matched.item(i)->isStyleRule()) { + continue; + } + + CSSStyleRule* rule = static_cast<CSSStyleRule*>(matched.item(i)); + String selector_text = rule->selectorText(); + String css_text; + + CSSMutableStyleDeclaration* style = rule->style(); + if (style) { + css_text = style->cssText(); + } + + OwnPtr<DictionaryValue> description(new DictionaryValue()); + description->SetString(L"selector", + webkit_glue::StringToStdString(selector_text)); + description->SetString(L"cssText", + webkit_glue::StringToStdString(css_text)); + + CSSStyleSheet* parent_style_sheet = rule->parentStyleSheet(); + if (parent_style_sheet) { + description->SetString(L"parentStyleSheetHref", + webkit_glue::StringToStdString(parent_style_sheet->href())); + + Node* owner_node = parent_style_sheet->ownerNode(); + if (owner_node) { + description->SetString(L"parentStyleSheetOwnerNodeName", + webkit_glue::StringToStdString(owner_node->nodeName())); + } + } + + descriptionList.Append(description.release()); + } +} + +void DomAgentImpl::BuildValueForAttributeStyles(const NamedNodeMap& attributes, + DictionaryValue& description) { + for (size_t i = 0; i < attributes.length(); ++i) { + Attribute* attr = attributes.attributeItem(i); + + if (CSSStyleDeclaration* style = attr->style()) { + std::wstring name = + webkit_glue::StringToStdWString(attr->name().toString()); + std::string css_text = + webkit_glue::StringToStdString(style->cssText()); + + description.SetString(name, css_text); + } + } +} + ListValue* DomAgentImpl::BuildValueForNode(Node* node, int depth) { OwnPtr<ListValue> value(new ListValue()); int id = Bind(node); @@ -485,7 +580,7 @@ ListValue* DomAgentImpl::BuildValueForElementAttributes(Element* element) { return attributes_value.release(); } unsigned num_attrs = attr_map->length(); - for (unsigned i = 0; i < num_attrs; i++) { + for (unsigned i = 0; i < num_attrs; ++i) { // Add attribute pair const Attribute *attribute = attr_map->attributeItem(i); OwnPtr<Value> name(Value::CreateStringValue( diff --git a/webkit/glue/devtools/dom_agent_impl.h b/webkit/glue/devtools/dom_agent_impl.h index 89a9df9..9fa011c 100644 --- a/webkit/glue/devtools/dom_agent_impl.h +++ b/webkit/glue/devtools/dom_agent_impl.h @@ -16,12 +16,15 @@ #include "webkit/glue/devtools/dom_agent.h" namespace WebCore { +class CSSRuleList; class Document; class Element; class Event; +class NameNodeMap; class Node; } +class DictionaryValue; class ListValue; class Value; @@ -48,6 +51,7 @@ class DomAgentImpl : public DomAgent { int element_id, const WebCore::String& value); void PerformSearch(int call_id, const String& query); + void GetNodeStyles(int call_id, int id, bool author_only); void DiscardBindings(); // Initializes dom agent with the given document. @@ -124,6 +128,12 @@ class DomAgentImpl : public DomAgent { int InnerChildNodeCount(WebCore::Node* node); WebCore::Element* InnerParentElement(WebCore::Node* node); + // Helpers for GetNodeStyles + void BuildValueForCSSRules(WebCore::CSSRuleList& matched, + ListValue& descriptionList); + void BuildValueForAttributeStyles(const WebCore::NamedNodeMap& attributes, + DictionaryValue& description); + DomAgentDelegate* delegate_; HashMap<WebCore::Node*, int> node_to_id_; HashMap<int, WebCore::Node*> id_to_node_; diff --git a/webkit/glue/devtools/js/devtools_host_stub.js b/webkit/glue/devtools/js/devtools_host_stub.js index 73b290e..6bc9690 100644 --- a/webkit/glue/devtools/js/devtools_host_stub.js +++ b/webkit/glue/devtools/js/devtools_host_stub.js @@ -127,6 +127,21 @@ RemoteDomAgentStub.prototype.DiscardBindings = function() { }; +RemoteDomAgentStub.prototype.GetNodeStyles = function(callId, id, authorOnly) { + var styles = { + computedStyle: "display: none", + inlineStyle: "display: none", + styleAttributes: {attr: "display: none"}, + matchedCSSRules: [{selector: "S", cssText: "display: none", + parentStyleSheetHref: "http://localhost", + parentStyleSheetOwnerNodeName: "DIV"}] + }; + setTimeout(function() { + RemoteDomAgent.DidGetNodeStyles(callId, styles); + }, 0); +}; + + /** * @constructor */ diff --git a/webkit/glue/devtools/js/dom_agent.js b/webkit/glue/devtools/js/dom_agent.js index 880d508..eabdff6 100644 --- a/webkit/glue/devtools/js/dom_agent.js +++ b/webkit/glue/devtools/js/dom_agent.js @@ -56,6 +56,9 @@ devtools.DomNode = function(doc, payload) { this.firstChild = null; this.parentNode = null; + this.styles_ = null; + this.disabledStyleProperties_ = {}; + if (payload.length > devtools.PayloadIndex.CHILD_NODES) { // Has children payloads this.setChildrenPayload_( @@ -93,9 +96,7 @@ devtools.DomNode.prototype = { */ devtools.DomNode.prototype.setAttributesPayload_ = function(attrs) { for (var i = 0; i < attrs.length; i += 2) { - var attr = {"name" : attrs[i], "value" : attrs[i+1]}; - this.attributes.push(attr); - this.attributesMap_[attrs[i]] = attr; + this.addAttribute_(attrs[i], attrs[i + 1]); } }; @@ -208,13 +209,31 @@ devtools.DomNode.prototype.setAttribute = function(name, value) { if (attr) { attr.value = value; } else { - attr = {"name" : name, "value" : value}; - self.attributesMap_[name] = attr; - self.attributes_.push(attr); + attr = self.addAttribute_(name, value); } }); }; +/** + * Creates an attribute-like object and adds it to the object. + * @param {string} name Attribute name to set value for. + * @param {string} value Attribute value to set. + */ +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]; + } + }; + + this.attributesMap_[name] = attr; + this.attributes.push(attr); +}; + /** * Sends 'remove attribute' command to the remote agent. @@ -235,11 +254,114 @@ 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 + * - node.document.defaultView.getComputedStyles(node) + * - node.document.defaultView.getMatchedCSSRules(node, ...) + * - style attribute of node's attributes + * @param {string} computedStyle is a cssText of result of getComputedStyle(). + * @param {string} inlineStyle is a style.cssText (defined in the STYLE + * attribute). + * @param {Object} styleAttributes represents 'style' property + * of attributes. + * @param {Array.<object>} matchedCSSRules represents result of the + * getMatchedCSSRules(node, "", authorOnly). Each elemet consists of: + * selector, rule.style.cssText[, rule.parentStyleSheet.href + * [, rule.parentStyleSheet.ownerNode.nodeName]]. + */ +devtools.DomNode.prototype.setStyles_ = function(computedStyle, inlineStyle, + styleAttributes, matchedCSSRules) { + var styles = {}; + styles.computedStyle = this.parseCSSText_(computedStyle, "computed"); + styles.inlineStyle = this.parseCSSText_(inlineStyle, "inline"); + + styles.attributes = {}; + for (var name in styleAttributes) { + var style = this.parseCSSText_(styleAttributes[name], "@" + name); + styles.attributes[name] = style; + } + + styles.matchedCSSRules = []; + for (var i = 0; i < matchedCSSRules.length; i++) { + var descr = matchedCSSRules[i]; + var selector = descr.selector; + var style = this.parseCSSText_(descr.cssText, "CSSRule#" + selector); + + var parentStyleSheet = undefined; + if (descr.parentStyleSheetHref) { + parentStyleSheet = {href: descr.parentStyleSheetHref}; + + if (descr.parentStyleSheetOwnerNodeName) { + parentStyleSheet.ownerNode = + {nodeName: descr.parentStyleSheetOwnerNodeName}; + } + } + + styles.matchedCSSRules.push({selectorText: selector, "style": style, + "parentStyleSheet": parentStyleSheet}); + } + + this.styles_ = styles; +} + + +/** + * Creates a style object from the cssText. + * Since the StyleSidebarPane implies the + * style object lives as long as the node itself and stores data in + * __disabledPropertyPriorities this methods adds a getter which stores the + * data in the devtools.DomNode object. + * @param {string} cssText + * @param {string} styleId is used to distinguish associated part of + * __disabledPropertyPriorities with the style object. + * @return {CSSStyleDescription} + */ +devtools.DomNode.prototype.parseCSSText_ = function(cssText, styleId) { + // There is no way to create CSSStyleDeclaration without creating a + // dummy element. In real DOM CSSStyleDeclaration has several + // implementations (for instance CSSComputedStyleDeclaration) and + // current method does not covers diffirences in behaviour. + // TODO (serya): correclty implement all types of CSSStyleDeclaration, + // avoid creation a lot of dummy nodes. + + var style = document.createElement("SPAN").style; + style.cssText = cssText; + + var props = this.disabledStyleProperties_[styleId] || {}; + this.disabledStyleProperties_[styleId] = props; + style.__disabledPropertyPriorities = props; + + return style; +} + + +/** + * Remove references to the style information to release + * resources when styles are not going to be used. + * @see setStyles_. + */ +devtools.DomNode.prototype.clearStyles_ = function() { + this.styles_ = null; +} + + +/** * Remote Dom document abstraction. * @param {devtools.DomAgent} domAgent owner agent. + * @param {devtools.DomWindow} defaultView owner window. * @constructor. */ -devtools.DomDocument = function(domAgent) { +devtools.DomDocument = function(domAgent, defaultView) { devtools.DomNode.call(this, null, [ 0, // id @@ -250,11 +372,8 @@ devtools.DomDocument = function(domAgent) { 0, // childNodeCount ]); this.listeners_ = {}; - this.defaultView = { - getComputedStyle : function() {}, - getMatchedCSSRules : function() {} - }; this.domAgent_ = domAgent; + this.defaultView = defaultView; }; goog.inherits(devtools.DomDocument, devtools.DomNode); @@ -312,6 +431,66 @@ devtools.DomDocument.prototype.fireDomEvent_ = function(name, event) { }; + +/** + * Simulation of inspected DOMWindow. + * @param {devtools.DomAgent} domAgent owner agent. + * @constructor + */ +devtools.DomWindow = function(domAgent) { + this.document = new devtools.DomDocument(domAgent, this); +}; + +/** + * Represents DOM Node class. + */ +devtools.DomWindow.prototype.__defineGetter__("Node", function() { + return devtools.DomNode; +}); + +/** + * Represents DOM Element class. + * @constructor + */ +devtools.DomWindow.prototype.__defineGetter__("Element", function() { + return devtools.DomNode; +}); + + +/** + * See usages in ScopeChainSidebarPane.js where it's called as + * constructor. + */ +devtools.DomWindow.prototype.Object = function() { +}; + + +/** + * Simulates the DOM interface for styles. Must be called after + * node.setStyles_. + * @param {devtools.DomNode} node + * @return {CSSStyleDescription} + */ +devtools.DomWindow.prototype.getComputedStyle = function(node) { + return node.styles_.computedStyle; +}; + + +/** + * Simulates the DOM interface for styles. Must be called after + * node.setStyles_. + * @param {devtools.DomNode} nodeStyles + * @param {string} pseudoElement assumed to be empty string. + * @param {boolean} authorOnly assumed to be equal to authorOnly argument of + * getNodeStylesAsync. + * @return {CSSStyleDescription} + */ +devtools.DomWindow.prototype.getMatchedCSSRules = function(node, + pseudoElement, authorOnly) { + return node.styles_.matchedCSSRules; +}; + + /** * Creates DomAgent Js representation. * @constructor @@ -323,6 +502,8 @@ devtools.DomAgent = function() { devtools.Callback.processCallback; RemoteDomAgent.DidApplyDomChange = devtools.Callback.processCallback; + RemoteDomAgent.DidGetNodeStyles = + devtools.Callback.processCallback; RemoteDomAgent.DidRemoveAttribute = devtools.Callback.processCallback; RemoteDomAgent.DidSetTextNodeValue = @@ -342,10 +523,10 @@ devtools.DomAgent = function() { /** * Top-level (and the only) document. - * @type {devtools.DomDocument} + * @type {devtools.DomWindow} * @private */ - this.document_ = null; + this.window_ = null; /** * Id to node mapping. @@ -368,17 +549,25 @@ devtools.DomAgent = function() { * Rests dom agent to its initial state. */ devtools.DomAgent.prototype.reset = function() { - this.document_ = new devtools.DomDocument(this); - this.idToDomNode_ = { 0 : this.document_ }; + this.window_ = new devtools.DomWindow(this); + this.idToDomNode_ = { 0 : this.getDocument() }; this.searchResults_ = []; }; /** - * @return {devtools.DomDocument} Top level (and the only) document. + * @return {devtools.DomWindow} Window for the top level (and the only) document. + */ +devtools.DomAgent.prototype.getWindow = function() { + return this.window_; +}; + + +/** + * @return {devtools.DomDocument} A document of the top level window. */ devtools.DomAgent.prototype.getDocument = function() { - return this.document_; + return this.window_.document; }; @@ -386,7 +575,7 @@ devtools.DomAgent.prototype.getDocument = function() { * Requests that the document element is sent from the agent. */ devtools.DomAgent.prototype.getDocumentElementAsync = function() { - if (this.document_.documentElement) { + if (this.getDocument().documentElement) { return; } RemoteDomAgent.GetDocumentElement(); @@ -503,13 +692,14 @@ devtools.DomAgent.prototype.getNodeForId = function(nodeId) { * {@inheritDoc}. */ devtools.DomAgent.prototype.setDocumentElement = function(payload) { - if (this.document_.documentElement) { + var doc = this.getDocument(); + if (doc.documentElement) { return; } this.setChildNodes(0, [payload]); - this.document_.documentElement = this.document_.firstChild; - this.document_.documentElement.ownerDocument = this.document_; - this.document_.fireDomEvent_("DOMContentLoaded"); + doc.documentElement = doc.firstChild; + doc.documentElement.ownerDocument = doc; + doc.fireDomEvent_("DOMContentLoaded"); }; @@ -561,7 +751,7 @@ devtools.DomAgent.prototype.childNodeInserted = function( var node = parent.insertChild_(prev, payload); this.idToDomNode_[node.id_] = node; var event = { target : node, relatedNode : parent }; - this.document_.fireDomEvent_("DOMNodeInserted", event); + this.getDocument().fireDomEvent_("DOMNodeInserted", event); }; @@ -575,7 +765,7 @@ devtools.DomAgent.prototype.childNodeRemoved = function( var node = this.idToDomNode_[nodeId]; parent.removeChild_(node); var event = { target : node, relatedNode : parent }; - this.document_.fireDomEvent_("DOMNodeRemoved", event); + this.getDocument().fireDomEvent_("DOMNodeRemoved", event); delete this.idToDomNode_[nodeId]; }; @@ -621,6 +811,40 @@ devtools.DomAgent.prototype.performSearchCallback_ = function(forEach, }; +/** + * Asyncronously requests all the information about styles for the node. + * @param {devtools.DomNode} node to get styles for. + * @param {boolean} authorOnly is a parameter for getMatchedCSSRules + * @param {function()} callback invoked while the node filled up with styles + */ +devtools.DomAgent.prototype.getNodeStylesAsync = function(node, + authorOnly, + callback) { + RemoteDomAgent.GetNodeStyles( + devtools.Callback.wrap( + goog.bind(this.getNodeStylesCallback_, this, node, callback)), + node.id_, authorOnly); +}; + + +/** + * Accepts results of RemoteDomAgent.GetNodeStyles + * @param {devtools.DomNode} node of the reveived styles. + * @param {function()} callback to notify the getNodeStylesAsync caller. + * @param {object} styles is structure representing all the styles. + */ +devtools.DomAgent.prototype.getNodeStylesCallback_ = function(node, + callback, styles) { + + node.setStyles_(styles.computedStyle, styles.inlineStyle, + styles.styleAttributes, styles.matchedCSSRules); + + callback(); + + node.clearStyles_(); +}; + + function firstChildSkippingWhitespace() { return this.firstChild; } diff --git a/webkit/glue/devtools/js/inspector_controller_impl.js b/webkit/glue/devtools/js/inspector_controller_impl.js index 72897f5..087ff2d 100644 --- a/webkit/glue/devtools/js/inspector_controller_impl.js +++ b/webkit/glue/devtools/js/inspector_controller_impl.js @@ -12,24 +12,6 @@ goog.provide('devtools.InspectorControllerImpl'); devtools.InspectorControllerImpl = function() { devtools.InspectorController.call(this); this.frame_element_id_ = 1; - - this.window_ = { - get document() { - return devtools.tools.getDomAgent().getDocument(); - }, - get Node() { - return devtools.DomNode; - }, - get Element() { - return devtools.DomNode; - }, - /** - * See usages in ScopeChainSidebarPane.js where it's called as - * constructor. - */ - Object : function() { - } - }; }; goog.inherits(devtools.InspectorControllerImpl, devtools.InspectorController); @@ -91,7 +73,7 @@ devtools.InspectorControllerImpl.prototype.highlightDOMNode = * {@inheritDoc}. */ devtools.InspectorControllerImpl.prototype.inspectedWindow = function() { - return this.window_; + return devtools.tools.getDomAgent().getWindow(); }; |