Reviewed by Adam.
authortimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Nov 2007 17:56:16 +0000 (17:56 +0000)
committertimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Nov 2007 17:56:16 +0000 (17:56 +0000)
        Bug 11920: Web Inspector should have Firebug-like CSS editing
        http://bugs.webkit.org/show_bug.cgi?id=11920

        * css/CSSComputedStyleDeclaration.h:
          (WebCore::CSSComputedStyleDeclaration::isPropertyImplicit): Return false. I'm not sure why
          this was true, but computed style has no concept of implicit. So false makes more sense
          and makes the code simpler in the inspector. This function was added for the inspector,
          so this isn't a compatibility change.
        * page/inspector/PropertiesSection.js: Add a getter/setter to reset populated status.
        * page/inspector/StylesSidebarPane.js: Some refactoring along with the main support for
          style editing.
        * page/inspector/inspector.css: Style changes for propery editing and focus correctness.
        * page/inspector/inspector.js: Look for a handleKeyEvent function of the focus element before
          trying to call a function based on the element's id. Call focused and blurred on the focused
          element when currentFocusElement is changed. Use the new listItemElement getter instead of
          the private property.
        * page/inspector/treeoutline.js: No longer expand on double click if ondblclick is implemented.
          Shrink the toggle zone to 10px to better match the size of the arrow. Add an onattach call
          to allow generation of the title using the DOM element. Add listItemElement and
          childrenListElement getters.
        * page/inspector/utilities.js: Add new helper prototype methods on CSSStyleDeclaration.
        * page/inspector/DocumentPanel.js: Use the new listItemElement getter instead of the private
          property. Also expand the DOM node on double click now that the TreeOutline dosen't do it.
        * page/inspector/Resource.js: Use the new listItemElement and childrenListElement getters
          instead of the private properties.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@27575 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/css/CSSComputedStyleDeclaration.h
WebCore/page/inspector/DocumentPanel.js
WebCore/page/inspector/PropertiesSection.js
WebCore/page/inspector/Resource.js
WebCore/page/inspector/StylesSidebarPane.js
WebCore/page/inspector/inspector.css
WebCore/page/inspector/inspector.js
WebCore/page/inspector/treeoutline.js
WebCore/page/inspector/utilities.js

index 0231f9d02b0e7e5ff909abbc683f740c979515d9..1461cd97d5f895f1a8e1932db188a100fc2a4813 100644 (file)
@@ -1,3 +1,33 @@
+2007-11-06  Timothy Hatcher  <timothy@apple.com>
+
+        Reviewed by Adam.
+
+        Bug 11920: Web Inspector should have Firebug-like CSS editing
+        http://bugs.webkit.org/show_bug.cgi?id=11920
+
+        * css/CSSComputedStyleDeclaration.h:
+          (WebCore::CSSComputedStyleDeclaration::isPropertyImplicit): Return false. I'm not sure why
+          this was true, but computed style has no concept of implicit. So false makes more sense
+          and makes the code simpler in the inspector. This function was added for the inspector,
+          so this isn't a compatibility change.
+        * page/inspector/PropertiesSection.js: Add a getter/setter to reset populated status.
+        * page/inspector/StylesSidebarPane.js: Some refactoring along with the main support for
+          style editing.
+        * page/inspector/inspector.css: Style changes for propery editing and focus correctness.
+        * page/inspector/inspector.js: Look for a handleKeyEvent function of the focus element before
+          trying to call a function based on the element's id. Call focused and blurred on the focused
+          element when currentFocusElement is changed. Use the new listItemElement getter instead of
+          the private property.
+        * page/inspector/treeoutline.js: No longer expand on double click if ondblclick is implemented.
+          Shrink the toggle zone to 10px to better match the size of the arrow. Add an onattach call
+          to allow generation of the title using the DOM element. Add listItemElement and
+          childrenListElement getters.
+        * page/inspector/utilities.js: Add new helper prototype methods on CSSStyleDeclaration.
+        * page/inspector/DocumentPanel.js: Use the new listItemElement getter instead of the private
+          property. Also expand the DOM node on double click now that the TreeOutline dosen't do it.
+        * page/inspector/Resource.js: Use the new listItemElement and childrenListElement getters
+          instead of the private properties.
+
 2007-11-07  Simon Hausmann  <hausmann@kde.org>
 
         Reviewed by Alexey Proskuryakov.
index 689ba357db383941cddf93439b4f1d53806109af..b19455c0f30a0214c36b3b37b8c4cf8c279a2fc5 100644 (file)
@@ -48,7 +48,7 @@ public:
     virtual String getPropertyValue(int propertyID) const;
     virtual bool getPropertyPriority(int propertyID) const;
     virtual int getPropertyShorthand(int propertyID) const { return -1; }
-    virtual bool isPropertyImplicit(int propertyID) const { return true; }
+    virtual bool isPropertyImplicit(int propertyID) const { return false; }
 
     virtual PassRefPtr<CSSMutableStyleDeclaration> copy() const;
     virtual PassRefPtr<CSSMutableStyleDeclaration> makeMutable();
index f80168f6977319443d99f17a9b88b36e851dcd69..5f8ca2d2c0d94b2dcdda2fd79e8b62ccafeb8f70 100644 (file)
@@ -396,16 +396,17 @@ WebInspector.DOMNodeTreeElement = function(node)
 WebInspector.DOMNodeTreeElement.prototype = {
     updateSelection: function()
     {
-        if (!this._listItemNode)
+        var listItemElement = this.listItemElement;
+        if (!listItemElement)
             return;
 
         if (!this.selectionElement) {
             this.selectionElement = document.createElement("div");
             this.selectionElement.className = "selection selected";
-            this._listItemNode.insertBefore(this.selectionElement, this._listItemNode.firstChild);
+            listItemElement.insertBefore(this.selectionElement, listItemElement.firstChild);
         }
 
-        this.selectionElement.style.height = this._listItemNode.offsetHeight + "px";
+        this.selectionElement.style.height = listItemElement.offsetHeight + "px";
     },
 
     onpopulate: function()
@@ -442,10 +443,9 @@ WebInspector.DOMNodeTreeElement.prototype = {
 
     onreveal: function()
     {
-        if (!this._listItemNode || !this.treeOutline || !this.treeOutline._childrenListNode)
+        if (!this.listItemElement || !this.treeOutline)
             return;
-        var parent = this.treeOutline.panel.views.dom.treeContentElement;
-        parent.scrollToElement(this._listItemNode);
+        this.treeOutline.panel.views.dom.treeContentElement.scrollToElement(this.listItemElement);
     },
 
     onselect: function()
@@ -465,6 +465,9 @@ WebInspector.DOMNodeTreeElement.prototype = {
         var panel = this.treeOutline.panel;
         panel.rootDOMNode = this.representedObject.parentNode;
         panel.focusedDOMNode = this.representedObject;
+
+        if (this.hasChildren && !this.expanded)
+            this.expand();
     }
 }
 
index bed9167212527cd89dc12eb98cb8a636fd3d5fdf..738240e20f2136d60cb10066fdf298c4ba75ca55 100644 (file)
@@ -99,17 +99,31 @@ WebInspector.PropertiesSection.prototype = {
             this.collapse();
     },
 
+    get populated()
+    {
+        return this._populated;
+    },
+
+    set populated(x)
+    {
+        this._populated = x;
+        if (!x && this.onpopulate && this._expanded) {
+            this.onpopulate(this);
+            this._populated = true;
+        }
+    },
+
     expand: function()
     {
         if (this._expanded)
             return;
         this._expanded = true;
+        this.element.addStyleClass("expanded");
 
         if (!this._populated && this.onpopulate) {
             this.onpopulate(this);
             this._populated = true;
         }
-        this.element.addStyleClass("expanded");
     },
 
     collapse: function()
index e365a14021df84108ecfc64b9ad5edf06d575013..7fb0a482587dd7bb874de0452dac508e3bb915bf 100644 (file)
@@ -684,7 +684,7 @@ WebInspector.ResourceTreeElement.deselected = function(element)
 
 WebInspector.ResourceTreeElement.revealed = function(element)
 {
-    if (!element._listItemNode || !element.treeOutline || !element.treeOutline._childrenListNode)
+    if (!element.listItemElement || !element.treeOutline || !element.treeOutline.childrenListElement)
         return;
-    element.treeOutline._childrenListNode.scrollToElement(element._listItemNode);
+    element.treeOutline.childrenListElement.scrollToElement(element.listItemElement);
 }
index afcedb746682756e8f75fdcfb66809e170aeb5ab..57c8da56c26a89bc87f1b38b7194689da5669aa8 100644 (file)
@@ -32,35 +32,52 @@ WebInspector.StylesSidebarPane = function()
 }
 
 WebInspector.StylesSidebarPane.prototype = {
-    update: function(node)
+    update: function(node, editedSection)
     {
-        var body = this.bodyElement;
+        var refresh = false;
+
+        if (!node || node === this.node)
+            refresh = true;
 
-        body.removeChildren();
+        if (node && node.nodeType === Node.TEXT_NODE && node.parentNode)
+            node = node.parentNode;
 
-        this.sections = [];
+        if (node && node.nodeType !== Node.ELEMENT_NODE)
+            node = null;
+
+        if (node)
+            this.node = node;
+        else
+            node = this.node;
+
+        var body = this.bodyElement;
+        if (!refresh || !node) {
+            body.removeChildren();
+            this.sections = [];
+        }
 
         if (!node)
             return;
 
-        if (node.nodeType === Node.TEXT_NODE && node.parentNode)
-            node = node.parentNode;
-
         var styleRules = [];
-        var styleProperties = [];
-
-        if (node.nodeType === Node.ELEMENT_NODE) {
-            var propertyCount = [];
 
+        if (refresh) {
+            for (var i = 0; i < this.sections.length; ++i) {
+                var section = this.sections[i];
+                if (section.computedStyle)
+                    section.styleRule.style = node.ownerDocument.defaultView.getComputedStyle(node);
+                var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle };
+                styleRules.push(styleRule);
+            }
+        } else {
             var computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
-            if (computedStyle && computedStyle.length)
-                styleRules.push({ computedStyle: true, selectorText: "Computed Style", style: computedStyle });
+            styleRules.push({ computedStyle: true, selectorText: "Computed Style", style: computedStyle, editable: false });
 
             var nodeName = node.nodeName.toLowerCase();
             for (var i = 0; i < node.attributes.length; ++i) {
                 var attr = node.attributes[i];
                 if (attr.style) {
-                    var attrStyle = { style: attr.style };
+                    var attrStyle = { style: attr.style, editable: false };
                     attrStyle.subtitle = "element\u2019s \u201C" + attr.name + "\u201D attribute";
                     attrStyle.selectorText = nodeName + "[" + attr.name;
                     if (attr.value.length)
@@ -82,89 +99,93 @@ WebInspector.StylesSidebarPane.prototype = {
                 for (var i = (matchedStyleRules.length - 1); i >= 0; --i)
                     styleRules.push(matchedStyleRules[i]);
             }
+        }
 
-            var usedProperties = {};
-            var priorityUsed = false;
+        var usedProperties = {};
+        var priorityUsed = false;
 
-            // Walk the style rules and make a list of all used and overloaded properties.
-            for (var i = 0; i < styleRules.length; ++i) {
+        // Walk the style rules and make a list of all used and overloaded properties.
+        for (var i = 0; i < styleRules.length; ++i) {
+            var styleRule = styleRules[i];
+            if (styleRule.computedStyle)
+                continue;
+
+            styleRule.usedProperties = {};
+
+            var style = styleRule.style;
+            for (var j = 0; j < style.length; ++j) {
+                var name = style[j];
+
+                if (!priorityUsed && style.getPropertyPriority(name).length)
+                    priorityUsed = true;
+
+                // If the property name is already used by another rule this is rule's
+                // property is overloaded, so don't add it to the rule's usedProperties.
+                if (!(name in usedProperties))
+                    styleRule.usedProperties[name] = true;
+
+                if (name === "font") {
+                    // The font property is not reported as a shorthand. Report finding the individual
+                    // properties so they are visible in computed style.
+                    // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15598 is fixed.
+                    styleRule.usedProperties["font-family"] = true;
+                    styleRule.usedProperties["font-size"] = true;
+                    styleRule.usedProperties["font-style"] = true;
+                    styleRule.usedProperties["font-variant"] = true;
+                    styleRule.usedProperties["font-weight"] = true;
+                    styleRule.usedProperties["line-height"] = true;
+                }
+            }
+
+            // Add all the properties found in this style to the used properties list.
+            // Do this here so only future rules are affect by properties used in this rule.
+            for (var name in styleRules[i].usedProperties)
+                usedProperties[name] = true;
+        }
+
+        if (priorityUsed) {
+            // Walk the properties again and account for !important.
+            var foundPriorityProperties = [];
+
+            // Walk in reverse to match the order !important overrides.
+            for (var i = (styleRules.length - 1); i >= 0; --i) {
                 if (styleRules[i].computedStyle)
                     continue;
 
-                styleRules[i].overloadedProperties = {};
-
                 var foundProperties = {};
-
                 var style = styleRules[i].style;
                 for (var j = 0; j < style.length; ++j) {
                     var name = style[j];
-                    var shorthand = style.getPropertyShorthand(name);
-                    var overloaded = (name in usedProperties);
 
-                    if (!priorityUsed && style.getPropertyPriority(name).length)
-                        priorityUsed = true;
-
-                    if (overloaded)
-                        styleRules[i].overloadedProperties[name] = true;
+                    // Skip duplicate properties in the same rule.
+                    if (name in foundProperties)
+                        continue;
 
                     foundProperties[name] = true;
-                    if (shorthand)
-                        foundProperties[shorthand] = true;
-
-                    if (name === "font") {
-                        // The font property is not reported as a shorthand. Report finding the individual
-                        // properties so they are visible in computed style.
-                        // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15598 is fixed.
-                        foundProperties["font-family"] = true;
-                        foundProperties["font-size"] = true;
-                        foundProperties["font-style"] = true;
-                        foundProperties["font-variant"] = true;
-                        foundProperties["font-weight"] = true;
-                        foundProperties["line-height"] = true;
-                    }
-                }
-
-                // Add all the properties found in this style to the used properties list.
-                // Do this here so only future rules are affect by properties used in this rule.
-                for (var name in foundProperties)
-                    usedProperties[name] = true;
-            }
-
-            if (priorityUsed) {
-                // Walk the properties again and account for !important.
-                var foundPriorityProperties = [];
 
-                // Walk in reverse to match the order !important overrides.
-                for (var i = (styleRules.length - 1); i >= 0; --i) {
-                    if (styleRules[i].computedStyle)
-                        continue;
-
-                    var foundProperties = {};
-                    var style = styleRules[i].style;
-                    for (var j = 0; j < style.length; ++j) {
-                        var name = style[j];
-
-                        // Skip duplicate properties in the same rule.
-                        if (name in foundProperties)
-                            continue;
-
-                        foundProperties[name] = true;
-
-                        if (style.getPropertyPriority(name).length) {
-                            if (!(name in foundPriorityProperties))
-                                delete styleRules[i].overloadedProperties[name];
-                            else
-                                styleRules[i].overloadedProperties[name] = true;
-                            foundPriorityProperties[name] = true;
-                        } else if (name in foundPriorityProperties)
-                            styleRules[i].overloadedProperties[name] = true;
-                    }
+                    if (style.getPropertyPriority(name).length) {
+                        if (!(name in foundPriorityProperties))
+                            styleRules[i].usedProperties[name] = true;
+                        else
+                            delete styleRules[i].usedProperties[name];
+                        foundPriorityProperties[name] = true;
+                    } else if (name in foundPriorityProperties)
+                        delete styleRules[i].usedProperties[name];
                 }
             }
+        }
 
+        if (refresh) {
+            // Walk the style rules and update the sections with new overloaded and used properties.
+            for (var i = 0; i < styleRules.length; ++i) {
+                var styleRule = styleRules[i];
+                var section = styleRule.section;
+                section._usedProperties = (styleRule.usedProperties || usedProperties);
+                section.update((section === editedSection) || styleRule.computedStyle);
+            }
+        } else {
             // Make a property section for each style rule.
-            var styleRulesLength = styleRules.length;
-            for (var i = 0; i < styleRulesLength; ++i) {
+            for (var i = 0; i < styleRules.length; ++i) {
                 var styleRule = styleRules[i];
                 var subtitle = styleRule.subtitle;
                 delete styleRule.subtitle;
@@ -172,34 +193,42 @@ WebInspector.StylesSidebarPane.prototype = {
                 var computedStyle = styleRule.computedStyle;
                 delete styleRule.computedStyle;
 
-                var overloadedProperties = styleRule.overloadedProperties;
-                delete styleRule.overloadedProperties;
+                var ruleUsedProperties = styleRule.usedProperties;
+                delete styleRule.usedProperties;
+
+                var editable = styleRule.editable;
+                delete styleRule.editable;
 
-                var section = new WebInspector.StylePropertiesSection(styleRule, subtitle, computedStyle, (overloadedProperties || usedProperties));
+                // Default editable to true if it was omitted.
+                if (typeof editable === "undefined")
+                    editable = true;
+
+                var section = new WebInspector.StylePropertiesSection(styleRule, subtitle, computedStyle, (ruleUsedProperties || usedProperties), editable);
                 section.expanded = true;
+                section.pane = this;
 
                 body.appendChild(section.element);
                 this.sections.push(section);
             }
-        } else {
-            // can't style this node
         }
     }
 }
 
 WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
 
-WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, overloadedOrUsedProperties)
+WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable)
 {
     WebInspector.PropertiesSection.call(this, styleRule.selectorText);
 
     this.styleRule = styleRule;
     this.computedStyle = computedStyle;
+    this.editable = (editable && !computedStyle);
 
-    if (computedStyle)
-        this.usedProperties = overloadedOrUsedProperties;
-    else
-        this.overloadedProperties = overloadedOrUsedProperties || {};
+    // Prevent editing the user agent rules.
+    if (this.styleRule.parentStyleSheet && !this.styleRule.parentStyleSheet.ownerNode)
+        this.editable = false;
+
+    this._usedProperties = usedProperties;
 
     if (computedStyle) {
         if (Preferences.showInheritedComputedStyleProperties)
@@ -242,210 +271,391 @@ WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl
 }
 
 WebInspector.StylePropertiesSection.prototype = {
+    get usedProperties()
+    {
+        return this._usedProperties || {};
+    },
+
+    set usedProperties(x)
+    {
+        this._usedProperties = x;
+        this.update();
+    },
+
+    isPropertyInherited: function(property)
+    {
+        if (!this.computedStyle || !this._usedProperties)
+            return false;
+        // These properties should always show for Computed Style.
+        var alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
+        return !(property in this.usedProperties) && !(property in alwaysShowComputedProperties);
+    },
+
+    isPropertyOverloaded: function(property, shorthand)
+    {
+        if (this.computedStyle || !this._usedProperties)
+            return false;
+
+        var used = (property in this.usedProperties);
+        if (used || !shorthand)
+            return !used;
+
+        // Find out if any of the individual longhand properties of the shorthand
+        // are used, if none are then the shorthand is overloaded too.
+        var longhandProperties = this.styleRule.style.getLonghandProperties(property);
+        for (var j = 0; j < longhandProperties.length; ++j) {
+            var individualProperty = longhandProperties[j];
+            if (individualProperty in this.usedProperties)
+                return false;
+        }
+
+        return true;
+    },
+
+    update: function(full)
+    {
+        if (full || this.computedStyle) {
+            this.propertiesTreeOutline.removeChildren();
+            this.populated = false;
+        } else {
+            var child = this.propertiesTreeOutline.children[0];
+            while (child) {
+                child.overloaded = this.isPropertyOverloaded(child.name, child.shorthand);
+                child = child.traverseNextTreeElement(false, null, true);
+            }
+        }
+    },
+
     onpopulate: function()
     {
         var style = this.styleRule.style;
         if (!style.length)
             return;
 
-        var foundProperties = {};
+        var foundShorthands = {};
+        var uniqueProperties = style.getUniqueProperties();
+        uniqueProperties.sort();
 
-        // Add properties in reverse order to better match how the style
-        // system picks the winning value for duplicate properties.
-        for (var i = (style.length - 1); i >= 0; --i) {
-            var name = style[i];
+        for (var i = 0; i < uniqueProperties.length; ++i) {
+            var name = uniqueProperties[i];
             var shorthand = style.getPropertyShorthand(name);
 
-            if (name in foundProperties || (shorthand && shorthand in foundProperties))
+            if (shorthand && shorthand in foundShorthands)
                 continue;
 
-            foundProperties[name] = true;
-            if (shorthand)
-                foundProperties[shorthand] = true;
-
-            if (this.computedStyle)
-                var inherited = (this.usedProperties && !((shorthand || name) in this.usedProperties));
-            else {
-                var overloaded = ((shorthand || name) in this.overloadedProperties);
-
-                if (shorthand && !overloaded) {
-                    // Find out if all the individual properties of a shorthand
-                    // are overloaded and mark the shorthand as overloaded too.
-
-                    var count = 0;
-                    var overloadCount = 0;
-                    for (var j = 0; j < style.length; ++j) {
-                        var individualProperty = style[j];
-                        if (style.getPropertyShorthand(individualProperty) !== shorthand)
-                            continue;
-                        ++count;
-                        if (individualProperty in this.overloadedProperties)
-                            ++overloadCount;
-                    }
-
-                    overloaded = (overloadCount >= count);
-                }
+            if (shorthand) {
+                foundShorthands[shorthand] = true;
+                name = shorthand;
             }
 
-            var item = new WebInspector.StylePropertyTreeElement(style, (shorthand || name), this.computedStyle, (shorthand ? true : false), (overloaded || inherited));
-            this.propertiesTreeOutline.insertChild(item, 0);
+            var isShorthand = (shorthand ? true : false);
+            var inherited = this.isPropertyInherited(name);
+            var overloaded = this.isPropertyOverloaded(name, isShorthand);
+
+            var item = new WebInspector.StylePropertyTreeElement(style, name, isShorthand, inherited, overloaded);
+            this.propertiesTreeOutline.appendChild(item);
         }
     }
 }
 
 WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
 
-WebInspector.StylePropertyTreeElement = function(style, name, computedStyle, shorthand, overloadedOrInherited)
+WebInspector.StylePropertyTreeElement = function(style, name, shorthand, inherited, overloaded)
 {
-    // These properties should always show for Computed Style
-    var alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
-
-    // "Nicknames" for some common values that are easier to read.
-    var valueNicknames = {
-        "rgb(0, 0, 0)": "black",
-        "#000": "black",
-        "#000000": "black",
-        "rgb(255, 255, 255)": "white",
-        "#fff": "white",
-        "#ffffff": "white",
-        "#FFF": "white",
-        "#FFFFFF": "white",
-        "rgba(0, 0, 0, 0)": "transparent",
-        "rgb(255, 0, 0)": "red",
-        "rgb(0, 255, 0)": "lime",
-        "rgb(0, 0, 255)": "blue",
-        "rgb(255, 255, 0)": "yellow",
-        "rgb(255, 0, 255)": "magenta",
-        "rgb(0, 255, 255)": "cyan"
-    };
-
     this.style = style;
     this.name = name;
-    this.computedStyle = computedStyle;
     this.shorthand = shorthand;
-    this.overloaded = (!computedStyle && overloadedOrInherited);
-    this.inherited = (computedStyle && overloadedOrInherited && !(name in alwaysShowComputedProperties));
+    this._inherited = inherited;
+    this._overloaded = overloaded;
 
-    var priority = style.getPropertyPriority(name);
-    var value = style.getPropertyValue(name);
-    var htmlValue = value;
+    // Pass an empty title, the title gets made later in onattach.
+    TreeElement.call(this, "", null, shorthand);
+}
 
-    if (priority && !priority.length)
-        delete priority;
+WebInspector.StylePropertyTreeElement.prototype = {
+    get inherited()
+    {
+        return this._inherited;
+    },
 
-    if (!priority && shorthand) {
-        // Priority is not returned for shorthands, find the priority from an individual property.
-        for (var i = 0; i < style.length; ++i) {
-            var individualProperty = style[i];
-            if (style.getPropertyShorthand(individualProperty) !== name)
-                continue;
-            priority = style.getPropertyPriority(individualProperty);
-            break;
-        }
-    }
+    set inherited(x)
+    {
+        if (x === this._inherited)
+            return;
+        this._inherited = x;
+        this.updateState();
+    },
+
+    get overloaded()
+    {
+        return this._overloaded;
+    },
 
-    if (value) {
-        var urls = value.match(/url\([^)]+\)/);
-        if (urls) {
-            for (var i = 0; i < urls.length; ++i) {
-                var url = urls[i].substring(4, urls[i].length - 1);
-                htmlValue = htmlValue.replace(urls[i], "url(" + WebInspector.linkifyURL(url) + ")");
+    set overloaded(x)
+    {
+        if (x === this._overloaded)
+            return;
+        this._overloaded = x;
+        this.updateState();
+    },
+
+    onattach: function()
+    {
+        this.updateTitle();
+    },
+
+    updateTitle: function()
+    {
+        // "Nicknames" for some common values that are easier to read.
+        var valueNicknames = {
+            "rgb(0, 0, 0)": "black",
+            "#000": "black",
+            "#000000": "black",
+            "rgb(255, 255, 255)": "white",
+            "#fff": "white",
+            "#ffffff": "white",
+            "#FFF": "white",
+            "#FFFFFF": "white",
+            "rgba(0, 0, 0, 0)": "transparent",
+            "rgb(255, 0, 0)": "red",
+            "rgb(0, 255, 0)": "lime",
+            "rgb(0, 0, 255)": "blue",
+            "rgb(255, 255, 0)": "yellow",
+            "rgb(255, 0, 255)": "magenta",
+            "rgb(0, 255, 255)": "cyan"
+        };
+
+        var priority = (this.shorthand ? this.style.getShorthandPriority(this.name) : this.style.getPropertyPriority(this.name));
+        var value = (this.shorthand ? this.style.getShorthandValue(this.name) : this.style.getPropertyValue(this.name));
+        var htmlValue = value;
+
+        if (priority && !priority.length)
+            delete priority;
+        if (priority)
+            priority = "!" + priority;
+
+        if (value) {
+            var urls = value.match(/url\([^)]+\)/);
+            if (urls) {
+                for (var i = 0; i < urls.length; ++i) {
+                    var url = urls[i].substring(4, urls[i].length - 1);
+                    htmlValue = htmlValue.replace(urls[i], "url(" + WebInspector.linkifyURL(url) + ")");
+                }
+            } else {
+                if (value in valueNicknames)
+                    htmlValue = valueNicknames[value];
+                htmlValue = htmlValue.escapeHTML();
             }
-        } else {
-            if (value in valueNicknames)
-                htmlValue = valueNicknames[value];
-            htmlValue = htmlValue.escapeHTML();
-        }
-    } else if (shorthand) {
-        // Some shorthands (like border) return a null value, so compute a shorthand value.
-        // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed.
+        } else
+            htmlValue = value = "";
 
-        value = "";
+        this.updateState();
 
-        var foundProperties = {};
-        for (var i = 0; i < style.length; ++i) {
-            var individualProperty = style[i];
-            if (style.getPropertyShorthand(individualProperty) !== name || individualProperty in foundProperties)
-                continue;
+        var nameElement = document.createElement("span");
+        nameElement.className = "name";
+        nameElement.textContent = this.name;
 
-            var individualValue = style.getPropertyValue(individualProperty);
-            if (style.isPropertyImplicit(individualProperty) || individualValue === "initial")
-                continue;
+        var valueElement = document.createElement("span");
+        valueElement.className = "value";
+        valueElement.innerHTML = htmlValue;
 
-            foundProperties[individualProperty] = true;
+        if (priority) {
+            var priorityElement = document.createElement("span");
+            priorityElement.className = "priority";
+            priorityElement.textContent = priority;
+        }
 
-            if (value.length)
-                value += " ";
-            value += individualValue;
+        this.listItemElement.removeChildren();
+
+        this.listItemElement.appendChild(nameElement);
+        this.listItemElement.appendChild(document.createTextNode(": "));
+        this.listItemElement.appendChild(valueElement);
+
+        if (priorityElement) {
+            this.listItemElement.appendChild(document.createTextNode(" "));
+            this.listItemElement.appendChild(priorityElement);
         }
 
-        htmlValue = value.escapeHTML();
-    } else
-        htmlValue = value = "";
-
-    var classes = [];
-    if (!computedStyle && (style.isPropertyImplicit(name) || value === "initial"))
-        classes.push("implicit");
-    if (this.inherited)
-        classes.push("inherited");
-    if (this.overloaded)
-        classes.push("overloaded");
-
-    var title = "";
-    if (classes.length)
-        title += "<span class=\"" + classes.join(" ") + "\">";
-
-    title += "<span class=\"name\">" + name.escapeHTML() + "</span>: ";
-    title += "<span class=\"value\">" + htmlValue;
-    if (priority)
-        title += " !" + priority;
-    title += "</span>;";
-
-    if (value) {
-        // FIXME: this dosen't catch keyword based colors like black and white
-        var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(#[0-9a-fA-F]{3})/g);
-        if (colors) {
-            var colorsLength = colors.length;
-            for (var i = 0; i < colorsLength; ++i)
-                title += "<span class=\"swatch\" style=\"background-color: " + colors[i] + "\"></span>";
+        this.listItemElement.appendChild(document.createTextNode(";"));
+
+        if (value) {
+            // FIXME: this dosen't catch keyword based colors like black and white
+            var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(#[0-9a-fA-F]{3})/g);
+            if (colors) {
+                var colorsLength = colors.length;
+                for (var i = 0; i < colorsLength; ++i) {
+                    var swatchElement = document.createElement("span");
+                    swatchElement.className = "swatch";
+                    swatchElement.style.setProperty("background-color", colors[i]);
+                    this.listItemElement.appendChild(swatchElement);
+                }
+            }
         }
-    }
 
-    if (classes.length)
-        title += "</span>";
+        this.tooltip = this.name + ": " + (valueNicknames[value] || value) + (priority ? " " + priority : "");
+    },
 
-    TreeElement.call(this, title, null, shorthand);
+    updateState: function()
+    {
+        if (!this.listItemElement)
+            return;
 
-    this.tooltip = name + ": " + (valueNicknames[value] || value) + (priority ? " !" + priority : "");
-}
+        var value = (this.shorthand ? this.style.getShorthandValue(this.name) : this.style.getPropertyValue(this.name));
+        if (this.style.isPropertyImplicit(this.name) || value === "initial")
+            this.listItemElement.addStyleClass("implicit");
+        else
+            this.listItemElement.removeStyleClass("implicit");
+
+        if (this.inherited)
+            this.listItemElement.addStyleClass("inherited");
+        else
+            this.listItemElement.removeStyleClass("inherited");
+
+        if (this.overloaded)
+            this.listItemElement.addStyleClass("overloaded");
+        else
+            this.listItemElement.removeStyleClass("overloaded");
+    },
 
-WebInspector.StylePropertyTreeElement.prototype = {
     onpopulate: function()
     {
         // Only populate once and if this property is a shorthand.
         if (this.children.length || !this.shorthand)
             return;
 
-        var foundProperties = {};
+        var longhandProperties = this.style.getLonghandProperties(this.name);
+        for (var i = 0; i < longhandProperties.length; ++i) {
+            var name = longhandProperties[i];
 
-        // Add properties in reverse order to better match how the style
-        // system picks the winning value for duplicate properties.
-        for (var i = (this.style.length - 1); i >= 0; --i) {
-            var name = this.style[i];
-            var shorthand = this.style.getPropertyShorthand(name);
+            if (this.treeOutline.section) {
+                var inherited = this.treeOutline.section.isPropertyInherited(name);
+                var overloaded = this.treeOutline.section.isPropertyOverloaded(name);
+            }
 
-            if (shorthand !== this.name || name in foundProperties)
-                continue;
+            var item = new WebInspector.StylePropertyTreeElement(this.style, name, false, inherited, overloaded);
+            this.appendChild(item);
+        }
+    },
 
-            foundProperties[name] = true;
+    ondblclick: function(element, event)
+    {
+        this.startEditing(event.target);
+    },
 
-            if (this.computedStyle)
-                var inherited = (this.treeOutline.section.usedProperties && !(name in this.treeOutline.section.usedProperties));
-            else
-                var overloaded = (name in this.treeOutline.section.overloadedProperties);
+    startEditing: function(selectElement)
+    {
+        // FIXME: we don't allow editing of longhand properties under a shorthand right now.
+        if (this.parent.shorthand)
+            return;
+
+        if (this.editing || (this.treeOutline.section && !this.treeOutline.section.editable))
+            return;
+
+        this.editing = true;
+        this.previousTextContent = this.listItemElement.textContent;
 
-            var item = new WebInspector.StylePropertyTreeElement(this.style, name, this.computedStyle, false, (inherited || overloaded));
-            this.insertChild(item, 0);
+        this.listItemElement.addStyleClass("focusable");
+        this.listItemElement.addStyleClass("editing");
+        this.wasExpanded = this.expanded;
+        this.collapse();
+        // Lie about out children to prevent toggling on click.
+        this.hasChildren = false;
+
+        if (!selectElement)
+            selectElement = this.listItemElement;
+
+        window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
+
+        var treeElement = this;
+        this.listItemElement.blurred = function() { treeElement.endEditing() };
+        this.listItemElement.handleKeyEvent = function(event) {
+            if (event.keyIdentifier === "Enter") {
+                treeElement.endEditing();
+                event.preventDefault();
+            }
+        };
+
+        this.previousFocusElement = WebInspector.currentFocusElement;
+        WebInspector.currentFocusElement = this.listItemElement;
+    },
+
+    endEditing: function()
+    {
+        // Revert the changes done in startEditing().
+        delete this.listItemElement.blurred;
+        delete this.listItemElement.handleKeyEvent;
+
+        WebInspector.currentFocusElement = this.previousFocusElement;
+        delete this.previousFocusElement;
+
+        delete this.editing;
+
+        this.listItemElement.removeStyleClass("focusable");
+        this.listItemElement.removeStyleClass("editing");
+        this.hasChildren = (this.children.length ? true : false);
+        if (this.wasExpanded) {
+            delete this.wasExpanded;
+            this.expand();
+        }
+
+        var previousContent = this.previousTextContent;
+        delete this.previousTextContent;
+
+        var userInput = this.listItemElement.textContent;
+        if (userInput === previousContent)
+            return; // nothing changed, so do nothing else
+
+        // Remove the original property from the real style declaration, if this represents
+        // a shorthand remove all the longhand properties.
+        if (this.shorthand) {
+            var longhandProperties = this.style.getLonghandProperties(this.name);
+            for (var i = 0; i < longhandProperties.length; ++i)
+                this.style.removeProperty(longhandProperties[i]);
+        } else
+            this.style.removeProperty(this.name);
+
+        // Create a new element to parse the user input CSS.
+        var parseElement = document.createElement("span");
+        parseElement.setAttribute("style", userInput);
+
+        var userInputStyle = parseElement.style;
+        if (userInputStyle.length) {
+            // Iterate of the properties on the test element's style declaration and
+            // add them to the real style declaration. We take care to move shorthands.
+
+            var foundShorthands = {};
+            var uniqueProperties = userInputStyle.getUniqueProperties();
+
+            for (var i = 0; i < uniqueProperties.length; ++i) {
+                var name = uniqueProperties[i];
+                var shorthand = userInputStyle.getPropertyShorthand(name);
+
+                if (shorthand && shorthand in foundShorthands)
+                    continue;
+
+                if (shorthand) {
+                    var value = userInputStyle.getShorthandValue(shorthand);
+                    var priority = userInputStyle.getShorthandPriority(shorthand);
+                    foundShorthands[shorthand] = true;
+                } else {
+                    var value = userInputStyle.getPropertyValue(name);
+                    var priority = userInputStyle.getPropertyPriority(name);
+                }
+
+                // Set the property on the real style declaration.
+                this.style.setProperty((shorthand || name), value, priority);
+            }
+
+            if (this.treeOutline.section && this.treeOutline.section.pane)
+                this.treeOutline.section.pane.update(null, this.treeOutline.section);
+            else if (this.treeOutline.section)
+                this.treeOutline.section.update(true);
+            else
+                this.updateTitle(); // FIXME: this will not show new properties. But we don't hit his case yet.
+        } else {
+            if (this.treeOutline.section && this.treeOutline.section.pane)
+                this.treeOutline.section.pane.update();
+            this.parent.removeChild(this);
         }
     }
 }
index 199dee722cb6d52b80237745c18be95cc1868ee4..838f7e81acd1584cad7adc42fa02b19dda2321c5 100644 (file)
@@ -1021,7 +1021,7 @@ body.inactive #sidebar li.selected {
     text-decoration: underline;
 }
 
-.focused .content.tree li.selected * {
+body:not(.inactive) .focused .content.tree li.selected * {
     color: inherit;
 }
 
@@ -1103,6 +1103,7 @@ body.inactive #sidebar li.selected {
     text-overflow: ellipsis;
     overflow: hidden;
     -webkit-user-select: text;
+    outline: none;
 }
 
 .section .properties li.parent {
@@ -1139,21 +1140,38 @@ body.inactive #sidebar li.selected {
     margin-top: 1px;
 }
 
+.section .properties li.editing {
+    -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px;
+    outline: 1px solid rgb(66%, 66%, 66%);
+    background-color: white;
+    -webkit-user-modify: read-write-plaintext-only;
+    text-overflow: clip;
+    margin-left: 8px;
+    padding-left: 2px;
+    margin-bottom: -1px;
+    padding-bottom: 1px;
+    text-decoration: none !important;
+    opacity: 1.0 !important;
+}
+
+.section .properties li.editing.parent::before {
+    display: none;
+}
+
+.section .properties li.editing * {
+    color: black !important;
+}
+
 .section .properties .overloaded {
     text-decoration: line-through;
 }
 
-.section .properties .implicit {
+.section .properties .implicit, .section .properties .inherited {
     opacity: 0.5;
 }
 
-.section .properties .inherited {
+.section:not(.show-inherited) .properties .inherited {
     display: none;
-    opacity: 0.5;
-}
-
-.section.show-inherited .properties .inherited {
-    display: inline;
 }
 
 .section .properties .name {
@@ -1164,6 +1182,10 @@ body.inactive #sidebar li.selected {
     color: blue;
 }
 
+.section .properties .priority {
+    color: rgb(128, 0, 0);
+}
+
 .section .properties .keyword {
     color: rgb(136, 19, 79);
 }
index 183de020edde44cfff309c51e74f618097353cdf..8f0b6d4cd309e9b058c58f7d12c15f36cbcd1e21 100644 (file)
@@ -88,6 +88,8 @@ var WebInspector = {
         if (this._currentFocusElement) {
             this._currentFocusElement.removeStyleClass("focused");
             this._currentFocusElement.addStyleClass("blurred");
+            if (this._currentFocusElement.blurred)
+                this._currentFocusElement.blurred();
         }
 
         this._currentFocusElement = x;
@@ -95,6 +97,8 @@ var WebInspector = {
         if (x) {
             x.addStyleClass("focused");
             x.removeStyleClass("blurred");
+            if (this._currentFocusElement.focused)
+                this._currentFocusElement.focused();
         }
     },
 
@@ -319,9 +323,11 @@ WebInspector.documentClick = function(event)
 
 WebInspector.documentKeypress = function(event)
 {
-    if (!this.currentFocusElement || !this.currentFocusElement.id || !this.currentFocusElement.id.length)
+    if (!this.currentFocusElement)
         return;
-    if (this.currentFocusElement.id + "Keypress" in WebInspector)
+    if (this.currentFocusElement.handleKeyEvent)
+        this.currentFocusElement.handleKeyEvent(event);
+    else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Keypress"])
         WebInspector[this.currentFocusElement.id + "Keypress"](event);
 }
 
@@ -391,7 +397,7 @@ WebInspector.animateStyle = function(animations, duration, callback, complete)
                 element = animation[key];
             else if (key === "start")
                 start = animation[key];
-            else if (key == "current")
+            else if (key === "current")
                 current = animation[key];
             else if (key === "end")
                 end = animation[key];
@@ -815,14 +821,14 @@ WebInspector.performSearch = function(query)
 
         WebInspector.navigateToPanel(element.representedObject.panel, "source");
         element.representedObject.line.scrollIntoView(true);
-        resultsContainer.scrollToElement(element._listItemNode);
+        resultsContainer.scrollToElement(element.listItemElement);
     }
 
     var domResultSelected = function(element)
     {
         WebInspector.navigateToPanel(element.representedObject.panel, "dom");
         element.representedObject.panel.focusedDOMNode = element.representedObject.node;
-        resultsContainer.scrollToElement(element._listItemNode);
+        resultsContainer.scrollToElement(element.listItemElement);
     }
 
     for (var i = 0; i < files.length; ++i) {
index ecb85aadc40d355b6d97930611b6aa78ddd65a83..a9e51a451cd25c44eb753dc0cb41877c701c540f 100644 (file)
@@ -401,6 +401,14 @@ function TreeElement(title, representedObject, hasChildren)
 }
 
 TreeElement.prototype = {
+    get listItemElement() {
+        return this._listItemNode;
+    },
+
+    get childrenListElement() {
+        return this._childrenListNode;
+    },
+
     get title() {
         return this._title;
     },
@@ -474,6 +482,9 @@ TreeElement.prototype._attach = function()
         this._listItemNode.addEventListener("mousedown", TreeElement.treeElementSelected, false);
         this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false);
         this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false);
+
+        if (this.onattach)
+            this.onattach(this);
     }
 
     this.parent._childrenListNode.insertBefore(this._listItemNode, (this.nextSibling ? this.nextSibling._listItemNode : null));
@@ -499,7 +510,7 @@ TreeElement.treeElementSelected = function(event)
     if (!element || !element.treeElement || !element.treeElement.selectable)
         return;
 
-    if (event.offsetX > 20 || !element.treeElement.hasChildren)
+    if (event.offsetX > 10 || !element.treeElement.hasChildren)
         element.treeElement.select();
 }
 
@@ -509,7 +520,7 @@ TreeElement.treeElementToggled = function(event)
     if (!element || !element.treeElement)
         return;
 
-    if (event.offsetX <= 20 && element.treeElement.hasChildren) {
+    if (event.offsetX <= 10 && element.treeElement.hasChildren) {
         if (element.treeElement.expanded) {
             if (event.altKey)
                 element.treeElement.collapseRecursively();
@@ -530,11 +541,10 @@ TreeElement.treeElementDoubleClicked = function(event)
     if (!element || !element.treeElement)
         return;
 
-    if (element.treeElement.hasChildren && !element.treeElement.expanded)
-        element.treeElement.expand();
-
     if (element.treeElement.ondblclick)
-        element.treeElement.ondblclick(element.treeElement);
+        element.treeElement.ondblclick(element.treeElement, event);
+    else if (element.treeElement.hasChildren && !element.treeElement.expanded)
+        element.treeElement.expand();
 }
 
 TreeElement.prototype.collapse = function()
@@ -581,10 +591,8 @@ TreeElement.prototype.expand = function()
         if (this.onpopulate)
             this.onpopulate(this);
 
-        for (var i = 0; i < this.children.length; ++i) {
-            var child = this.children[i];
-            child._attach();
-        }
+        for (var i = 0; i < this.children.length; ++i)
+            this.children[i]._attach();
 
         delete this.refreshChildren;
     }
index 0bfd25288ddb144636385fb5fd890250ad206896..982fb8a8882a6f167d1b3e462454d87b7c956ff8 100644 (file)
@@ -244,6 +244,82 @@ String.prototype.trimURL = function(baseURLDomain)
     return result;
 }
 
+CSSStyleDeclaration.prototype.getShorthandValue = function(shorthandProperty)
+{
+    var value = this.getPropertyValue(shorthandProperty);
+    if (!value) {
+        // Some shorthands (like border) return a null value, so compute a shorthand value.
+        // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed.
+
+        var foundProperties = {};
+        for (var i = 0; i < this.length; ++i) {
+            var individualProperty = this[i];
+            if (individualProperty in foundProperties || this.getPropertyShorthand(individualProperty) !== shorthandProperty)
+                continue;
+
+            var individualValue = this.getPropertyValue(individualProperty);
+            if (this.isPropertyImplicit(individualProperty) || individualValue === "initial")
+                continue;
+
+            foundProperties[individualProperty] = true;
+
+            if (!value)
+                value = "";
+            else if (value.length)
+                value += " ";
+            value += individualValue;
+        }
+    }
+    return value;
+}
+
+CSSStyleDeclaration.prototype.getShorthandPriority = function(shorthandProperty)
+{
+    var priority = this.getPropertyPriority(shorthandProperty);
+    if (!priority) {
+        for (var i = 0; i < this.length; ++i) {
+            var individualProperty = this[i];
+            if (this.getPropertyShorthand(individualProperty) !== shorthandProperty)
+                continue;
+            priority = this.getPropertyPriority(individualProperty);
+            break;
+        }
+    }
+    return priority;
+}
+
+CSSStyleDeclaration.prototype.getLonghandProperties = function(shorthandProperty)
+{
+    var properties = [];
+    var foundProperties = {};
+
+    for (var i = 0; i < this.length; ++i) {
+        var individualProperty = this[i];
+        if (individualProperty in foundProperties || this.getPropertyShorthand(individualProperty) !== shorthandProperty)
+            continue;
+        foundProperties[individualProperty] = true;
+        properties.push(individualProperty);
+    }
+
+    return properties;
+}
+
+CSSStyleDeclaration.prototype.getUniqueProperties = function()
+{
+    var properties = [];
+    var foundProperties = {};
+
+    for (var i = 0; i < this.length; ++i) {
+        var property = this[i];
+        if (property in foundProperties)
+            continue;
+        foundProperties[property] = true;
+        properties.push(property);
+    }
+
+    return properties;
+}
+
 function isNodeWhitespace()
 {
     if (!this || this.nodeType !== Node.TEXT_NODE)