Changeset 46690 in webkit


Ignore:
Timestamp:
Aug 2, 2009 12:41:56 AM (15 years ago)
Author:
abarth@webkit.org
Message:

2009-07-30 Joseph Pecoraro <joepeck02@gmail.com>

Reviewed by Timothy Hatcher.

Inspector: let me *edit* css styles in the web inspector.
https://bugs.webkit.org/show_bug.cgi?id=27124

  • English.lproj/localizedStrings.js:
  • inspector/front-end/ElementsPanel.js: (WebInspector.ElementsPanel): added property stylesheet pointing to 1 stylesheet added to the page if needed
  • inspector/front-end/StylesSidebarPane.js: (WebInspector.StylesSidebarPane.prototype.update): handle blank sections (WebInspector.StylesSidebarPane.prototype.addBlankSection): blank section in the correct position (WebInspector.StylesSidebarPane.prototype.appropriateSelectorForNode): helper to get a nice selector for the selectd node (WebInspector.StylePropertiesSection): (WebInspector.StylePropertiesSection.prototype.expand): (WebInspector.StylePropertiesSection.prototype.isPropertyInherited): (WebInspector.StylePropertiesSection.prototype.isPropertyOverloaded): (WebInspector.StylePropertiesSection.prototype.addNewBlankProperty): (WebInspector.StylePropertiesSection.prototype._dblclickEmptySpace): easily create new properties (WebInspector.StylePropertiesSection.prototype._dblclickSelector): (WebInspector.StylePropertiesSection.prototype.startEditingSelector): allow for editing selectors (WebInspector.StylePropertiesSection.prototype.editingSelectorCommitted): (WebInspector.StylePropertiesSection.prototype.editingSelectorCancelled): (WebInspector.StylePropertiesSection.prototype._doesSelectorAffectSelectedNode): helper to check if a selector applies to the selected node (WebInspector.BlankStylePropertiesSection.prototype._dblclick): (WebInspector.BlankStylePropertiesSection.prototype.startEditing): (WebInspector.BlankStylePropertiesSection.prototype.editingCancelled): (WebInspector.BlankStylePropertiesSection.prototype.editingCommitted): (WebInspector.BlankStylePropertiesSection.prototype.makeNormal): morph into a StylePropertiesSection (WebInspector.StylePropertyTreeElement.prototype): (WebInspector.StylePropertyTreeElement.prototype.):
  • inspector/front-end/inspector.css:
Location:
trunk/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r46689 r46690  
     12009-07-30  Joseph Pecoraro  <joepeck02@gmail.com>
     2
     3        Reviewed by Timothy Hatcher.
     4
     5        Inspector: let me *edit* css styles in the web inspector.
     6        https://bugs.webkit.org/show_bug.cgi?id=27124
     7
     8        * English.lproj/localizedStrings.js:
     9        * inspector/front-end/ElementsPanel.js:
     10        (WebInspector.ElementsPanel): added property stylesheet pointing to 1 stylesheet added to the page if needed
     11        * inspector/front-end/StylesSidebarPane.js:
     12        (WebInspector.StylesSidebarPane.prototype.update): handle blank sections
     13        (WebInspector.StylesSidebarPane.prototype.addBlankSection): blank section in the correct position
     14        (WebInspector.StylesSidebarPane.prototype.appropriateSelectorForNode): helper to get a nice selector for the selectd node
     15        (WebInspector.StylePropertiesSection):
     16        (WebInspector.StylePropertiesSection.prototype.expand):
     17        (WebInspector.StylePropertiesSection.prototype.isPropertyInherited):
     18        (WebInspector.StylePropertiesSection.prototype.isPropertyOverloaded):
     19        (WebInspector.StylePropertiesSection.prototype.addNewBlankProperty):
     20        (WebInspector.StylePropertiesSection.prototype._dblclickEmptySpace): easily create new properties
     21        (WebInspector.StylePropertiesSection.prototype._dblclickSelector):
     22        (WebInspector.StylePropertiesSection.prototype.startEditingSelector): allow for editing selectors
     23        (WebInspector.StylePropertiesSection.prototype.editingSelectorCommitted):
     24        (WebInspector.StylePropertiesSection.prototype.editingSelectorCancelled):
     25        (WebInspector.StylePropertiesSection.prototype._doesSelectorAffectSelectedNode): helper to check if a selector applies to the selected node
     26        (WebInspector.BlankStylePropertiesSection.prototype._dblclick):
     27        (WebInspector.BlankStylePropertiesSection.prototype.startEditing):
     28        (WebInspector.BlankStylePropertiesSection.prototype.editingCancelled):
     29        (WebInspector.BlankStylePropertiesSection.prototype.editingCommitted):
     30        (WebInspector.BlankStylePropertiesSection.prototype.makeNormal): morph into a StylePropertiesSection
     31        (WebInspector.StylePropertyTreeElement.prototype):
     32        (WebInspector.StylePropertyTreeElement.prototype.):
     33        * inspector/front-end/inspector.css:
     34
    1352009-07-31  Anton Muhin  <antonm@chromium.org>
    236
  • trunk/WebCore/inspector/front-end/ElementsPanel.js

    r46687 r46690  
    114114    this._contentLoadedEventListener = InspectorController.wrapCallback(this._contentLoaded.bind(this));
    115115
     116    this.stylesheet = null;
     117
    116118    this.reset();
    117119}
  • trunk/WebCore/inspector/front-end/StylesSidebarPane.js

    r46429 r46690  
    6969            for (var i = 0; i < this.sections.length; ++i) {
    7070                var section = this.sections[i];
     71                if (section._blank)
     72                    continue;
    7173                if (section.computedStyle)
    7274                    section.styleRule.style = node.ownerDocument.defaultView.getComputedStyle(node);
    73                 var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle };
     75                var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule };
    7476                styleRules.push(styleRule);
    7577            }
     
    9294            }
    9395
    94             if (node.style && (node.style.length || Object.hasProperties(node.style.__disabledProperties))) {
    95                 var inlineStyle = { selectorText: WebInspector.UIString("Inline Style Attribute"), style: node.style };
     96            // Always Show element's Style Attributes
     97            if (node.nodeType === Node.ELEMENT_NODE) {
     98                var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: node.style, isAttribute: true };
    9699                inlineStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", "style");
    97100                styleRules.push(inlineStyle);
     
    103106                for (var i = (matchedStyleRules.length - 1); i >= 0; --i) {
    104107                    var rule = matchedStyleRules[i];
    105                     styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet });
     108                    styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule });
    106109                }
    107110            }
     
    128131            var styleRule = styleRules[i];
    129132            if (styleRule.computedStyle)
     133                continue;
     134            if (styleRule.section && styleRule.section.noAffect)
    130135                continue;
    131136
     
    224229                delete styleRule.editable;
    225230
     231                var isAttribute = styleRule.isAttribute;
     232                delete styleRule.isAttribute;
     233
    226234                // Default editable to true if it was omitted.
    227235                if (typeof editable === "undefined")
     
    237245                else if (computedStyle)
    238246                    section.collapse(true);
     247                else if (isAttribute && styleRule.style.length === 0)
     248                    section.collapse(true);
    239249                else
    240250                    section.expand(true);
     
    243253                this.sections.push(section);
    244254            }
    245         }
     255
     256            this.addBlankSection();
     257        }
     258    },
     259
     260    addBlankSection: function()
     261    {
     262        var blankSection = new WebInspector.BlankStylePropertiesSection();
     263        blankSection.pane = this;
     264
     265        this.bodyElement.insertBefore(blankSection.element, this.bodyElement.firstChild.nextSibling.nextSibling); // 0 is computed, 1 is element.style
     266        var computed = this.sections.shift();
     267        var elementStyle = this.sections.shift();
     268        this.sections.unshift(blankSection);
     269        this.sections.unshift(elementStyle);
     270        this.sections.unshift(computed);
     271    },
     272
     273    appropriateSelectorForNode: function()
     274    {
     275        var node = this.node;
     276        if (!node)
     277            return;
     278
     279        if (node.id)
     280            return "#" + node.id;
     281
     282        if (node.className)
     283            return "." + node.className.replace(/\s+/, ".");
     284
     285        var nodeName = node.nodeName.toLowerCase();
     286        if (nodeName === "input" && node.type)
     287            return nodeName + "[type=\"" + node.type + "\"]";
     288
     289        return nodeName;
    246290    }
    247291}
     
    252296{
    253297    WebInspector.PropertiesSection.call(this, styleRule.selectorText);
     298    this.titleElement.addEventListener("click", function(e) { e.stopPropagation(); }, false);
     299    this.titleElement.addEventListener("dblclick", this._dblclickSelector.bind(this), false);
     300    this.element.addEventListener("dblclick", this._dblclickEmptySpace.bind(this), false);
    254301
    255302    this.styleRule = styleRule;
     303    this.rule = this.styleRule.rule;
    256304    this.computedStyle = computedStyle;
    257305    this.editable = (editable && !computedStyle);
     
    301349            else if (isUser)
    302350                subtitle = WebInspector.UIString("user stylesheet");
     351            else if (this.styleRule.parentStyleSheet === WebInspector.panels.elements.stylesheet)
     352                subtitle = WebInspector.UIString("via inspector");
    303353            else
    304354                subtitle = WebInspector.UIString("inline stylesheet");
     
    310360    this.identifier = styleRule.selectorText;
    311361    if (this.subtitle)
    312         this.identifier += ":" + this.subtitleElement.textContent;
     362        this.identifier += ":" + this.subtitleElement.textContent;   
    313363}
    314364
     
    327377    expand: function(dontRememberState)
    328378    {
     379        if (this._blank)
     380            return;
     381
    329382        WebInspector.PropertiesSection.prototype.expand.call(this);
    330383        if (dontRememberState)
     
    349402    isPropertyInherited: function(property)
    350403    {
    351         if (!this.computedStyle || !this._usedProperties)
     404        if (!this.computedStyle || !this._usedProperties || this.noAffect)
    352405            return false;
    353406        // These properties should always show for Computed Style.
     
    358411    isPropertyOverloaded: function(property, shorthand)
    359412    {
    360         if (this.computedStyle || !this._usedProperties)
     413        if (this.computedStyle || !this._usedProperties || this.noAffect)
    361414            return false;
    362415
     
    452505        item._newProperty = true;
    453506        return item;
     507    },
     508
     509    _dblclickEmptySpace: function(event)
     510    {
     511        this.expand();
     512        this.addNewBlankProperty().startEditing();
     513    },
     514
     515    _dblclickSelector: function(event)
     516    {
     517        if (!this.editable)
     518            return;
     519
     520        if (!this.rule && this.propertiesTreeOutline.children.length === 0) {
     521            this.expand();
     522            this.addNewBlankProperty().startEditing();
     523            return;
     524        }
     525
     526        if (!this.rule)
     527            return;
     528
     529        this.startEditingSelector();
     530        event.stopPropagation();
     531    },
     532
     533    startEditingSelector: function()
     534    {
     535        var element = this.titleElement;
     536        if (WebInspector.isBeingEdited(element))
     537            return;
     538
     539        var context = this.styleRule.selectorText;
     540        WebInspector.startEditing(this.titleElement, this.editingSelectorCommitted.bind(this), this.editingSelectorCancelled.bind(this), context);
     541        window.getSelection().setBaseAndExtent(element, 0, element, 1);
     542    },
     543
     544    editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
     545    {
     546        function moveToNextIfNeeded() {
     547            if (!moveDirection || moveDirection !== "forward")
     548                return;
     549
     550            this.expand();
     551            if (this.propertiesTreeOutline.children.length === 0)
     552                this.addNewBlankProperty().startEditing();
     553            else {
     554                var item = this.propertiesTreeOutline.children[0]
     555                item.startEditing(item.valueElement);
     556            }
     557        }
     558
     559        if (newContent === oldContent)
     560            return moveToNextIfNeeded.call(this);
     561
     562        try {
     563            var stylesheet = this.rule.parentStyleSheet;
     564            stylesheet.addRule(newContent);
     565            var newRule = stylesheet.cssRules[stylesheet.cssRules.length - 1];
     566            newRule.style.cssText = this.styleRule.style.cssText;
     567        } catch (e) {
     568            // Invalid Syntax for a Selector
     569            this.editingSelectorCancelled(element, context);
     570            return moveToNextIfNeeded.call(this);
     571        }
     572
     573        if (!this._doesSelectorAffectSelectedNode(newContent)) {
     574            this.noAffect = true;
     575            this.element.addStyleClass("no-affect");
     576        } else {
     577            delete this.noAffect;
     578            this.element.removeStyleClass("no-affect");
     579        }
     580
     581        var parentRules = this.rule.parentStyleSheet.cssRules;
     582        for (var i = 0; i < parentRules.length; ++i) {
     583            if (parentRules[i] === this.rule) {
     584                this.rule.parentStyleSheet.removeRule(i);
     585                break;
     586            }
     587        }
     588
     589        this.rule = newRule;
     590        this.styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, parentStyleSheet: newRule.parentStyleSheet, rule: newRule };
     591        this.pane.update(null, true);
     592        moveToNextIfNeeded.call(this);
     593    },
     594
     595    editingSelectorCancelled: function(element, context)
     596    {
     597        element.textContent = context;
     598    },
     599
     600    _doesSelectorAffectSelectedNode: function(selector)
     601    {
     602        var selectedNode = this.pane.node;
     603        var nodes = selectedNode.ownerDocument.querySelectorAll(selector);
     604        for (var i = 0; i < nodes.length; ++i) {
     605            if (nodes[i] === selectedNode)
     606                return true;
     607        }
     608
     609        return false;
    454610    }
    455611}
    456612
    457613WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
     614
     615WebInspector.BlankStylePropertiesSection = function()
     616{
     617    WebInspector.PropertiesSection.call(this, WebInspector.UIString("Double-Click to Add"), null);
     618
     619    this._blank = true;
     620    this._dblclickListener = this._dblclick.bind(this);
     621    this.element.addStyleClass("blank-section");
     622    this.titleElement.addStyleClass("blank-title");
     623    this.titleElement.addEventListener("click", function(e) { e.stopPropagation(); }, false);
     624    this.titleElement.addEventListener("dblclick", this._dblclickListener, false);
     625}
     626
     627WebInspector.BlankStylePropertiesSection.prototype = {
     628    _dblclick: function(event)
     629    {
     630        this.startEditing();
     631    },
     632
     633    startEditing: function()
     634    {
     635        var element = this.titleElement;
     636        if (WebInspector.isBeingEdited(element))
     637            return;
     638
     639        this.titleElement.textContent = this.pane.appropriateSelectorForNode();
     640        this.titleElement.removeStyleClass("blank-title");
     641        WebInspector.startEditing(this.titleElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), "");
     642        window.getSelection().setBaseAndExtent(element, 0, element, 1);
     643    },
     644
     645    editingCancelled: function()
     646    {
     647        this.titleElement.textContent = WebInspector.UIString("Double-Click to Add");
     648        this.titleElement.addStyleClass("blank-title");
     649    },
     650
     651    editingCommitted: function(element, newContent, oldContent, context)
     652    {
     653        var stylesheet = WebInspector.panels.elements.stylesheet;
     654        if (!stylesheet) {
     655            var inspectedDocument = InspectorController.inspectedWindow().document;
     656            var head = inspectedDocument.getElementsByTagName("head")[0];
     657            var styleElement = inspectedDocument.createElement("style");
     658            styleElement.type = "text/css";
     659            head.appendChild(styleElement);
     660            stylesheet = inspectedDocument.styleSheets[inspectedDocument.styleSheets.length - 1];
     661            WebInspector.panels.elements.stylesheet = stylesheet;
     662        }
     663
     664        try {
     665            stylesheet.addRule(newContent);
     666        } catch (e) {
     667            // Invalid Syntax for a Selector
     668            this.editingCancelled();
     669            return;
     670        }
     671
     672        var rule = stylesheet.cssRules[stylesheet.cssRules.length - 1];
     673        var styleRule = { section: this, style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule };
     674        this.makeNormal(styleRule);
     675
     676        if (!this._doesSelectorAffectSelectedNode(newContent)) {
     677            this.noAffect = true;
     678            this.element.addStyleClass("no-affect");
     679        }
     680
     681        this.subtitleElement.textContent = WebInspector.UIString("via inspector");
     682        this.expand();
     683
     684        this.pane.addBlankSection();
     685        this.addNewBlankProperty().startEditing();
     686    },
     687
     688    makeNormal: function(styleRule)
     689    {
     690        this.titleElement.removeEventListener("dblclick", this._dblclickListener, false);
     691        this.titleElement.addEventListener("dblclick", this._dblclickSelector.bind(this), false);
     692        this.element.addEventListener("dblclick", this._dblclickEmptySpace.bind(this), false);
     693        this.element.removeStyleClass("blank-section");
     694        delete this._blank;
     695        delete this._dblclick;
     696        delete this.startEditing;
     697        delete this.editingCancelled;
     698        delete this.editingCommitted;
     699        delete this._dblclickListener;
     700        delete this.makeNormal;
     701        this.styleRule = styleRule;
     702        this.rule = styleRule.rule;
     703        this.computedStyle = false;
     704        this.editable = true;
     705        // leftovers are: this.noAffect if applicable
     706    }
     707}
     708
     709WebInspector.BlankStylePropertiesSection.prototype.__proto__ = WebInspector.StylePropertiesSection.prototype;
    458710
    459711WebInspector.StylePropertyTreeElement = function(style, name, shorthand, inherited, overloaded, disabled)
     
    732984    {
    733985        this.startEditing(event.target);
     986        event.stopPropagation();
    734987    },
    735988
     
    8591112    editingCancelled: function(element, context)
    8601113    {
    861         if (this.originalCSSText) {
     1114        if (this._newProperty)
     1115            this.treeOutline.removeChild(this);
     1116        else if (this.originalCSSText) {
    8621117            this.style.cssText = this.originalCSSText;
    8631118
     
    8771132
    8781133        // Determine where to move to before making changes
    879         var newProperty = false;
    880         var moveToPropertyName;
     1134        var newProperty, moveToPropertyName, moveToSelector;
    8811135        var moveTo = (moveDirection === "forward" ? this.nextSibling : this.previousSibling);
    8821136        if (moveTo)
     
    8841138        else if (moveDirection === "forward")
    8851139            newProperty = true;
     1140        else if (moveDirection === "backward" && this.treeOutline.section.rule)
     1141            moveToSelector = true;
    8861142
    8871143        // Make the Changes and trigger the moveToNextCallback after updating
     
    9201176                var item = section.addNewBlankProperty();
    9211177                item.startEditing();
    922             }
     1178                return;
     1179            }
     1180
     1181            if (moveToSelector)
     1182                section.startEditingSelector();
    9231183        }
    9241184    },
     
    9991259        if (updateInterface)
    10001260            this.updateAll(true);
     1261
     1262        if (!this.rule)
     1263            WebInspector.panels.elements.treeOutline.update();
    10011264    }
    10021265}
  • trunk/WebCore/inspector/front-end/inspector.css

    r46402 r46690  
    11831183}
    11841184
     1185.section.no-affect .header {
     1186    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(167, 167, 167)), to(rgb(123, 123, 123)))
     1187}
     1188
    11851189.section .header::before {
    11861190    position: absolute;
     
    11921196}
    11931197
     1198.section.blank-section .header::before {
     1199    display: none;
     1200}
     1201
    11941202.section.expanded .header::before {
    11951203    content: url(Images/treeDownTriangleWhite.png);
     
    12011209    word-wrap: break-word;
    12021210    white-space: normal;
     1211}
     1212
     1213.section .header .title.blank-title {
     1214    font-style: italic;
    12031215}
    12041216
     
    12401252    list-style: none;
    12411253    background-color: white;
     1254    min-height: 18px;
     1255}
     1256
     1257.section.no-affect .properties li {
     1258    opacity: 0.5;
     1259}
     1260
     1261.section.no-affect .properties li.editing {
     1262    opacity: 1.0;
    12421263}
    12431264
Note: See TracChangeset for help on using the changeset viewer.