Changeset 121860 in webkit


Ignore:
Timestamp:
Jul 4, 2012 9:10:45 AM (12 years ago)
Author:
apavlov@chromium.org
Message:

Web Inspector: Emulate pseudo styles (hover etc.) of non-selected elements
https://bugs.webkit.org/show_bug.cgi?id=86630

Reviewed by Pavel Feldman.

Source/WebCore:

  • A map of pseudo-states for all bound DOM nodes is maintained in the backend and queried whenever StyleResolver

calculates the effective element style.

  • In the frontend, markers are introduced to distinguish elements that have forced pseudo styles set for them.

Additionally, dimmed markers are added for collapsed nodes, whose descendants have forced pseudo styles.
More ElementDecorator subtypes will be added for other types of markers.

Test: inspector/styles/force-pseudo-state.html

  • English.lproj/localizedStrings.js:
  • inspector/InspectorCSSAgent.cpp:

(WebCore::InspectorCSSAgent::InspectorCSSAgent):
(WebCore::InspectorCSSAgent::clearFrontend):
(WebCore::InspectorCSSAgent::reset):
(WebCore::InspectorCSSAgent::forcePseudoState):
(WebCore::InspectorCSSAgent::recalcStyleForPseudoStateIfNeeded):
(WebCore::InspectorCSSAgent::elementForId):
(WebCore::InspectorCSSAgent::didRemoveDocument):
(WebCore::InspectorCSSAgent::didRemoveDOMNode):
(WebCore::InspectorCSSAgent::resetPseudoStates):

  • inspector/InspectorCSSAgent.h:

(InspectorCSSAgent):

  • inspector/InspectorDOMAgent.cpp:

(WebCore::InspectorDOMAgent::unbind):
(WebCore::InspectorDOMAgent::didRemoveDOMNode):

  • inspector/front-end/ElementsPanel.js:

(WebInspector.ElementsPanel.get this):
(WebInspector.ElementsPanel):
(WebInspector.ElementsPanel.prototype._setPseudoClassForNodeId):

  • inspector/front-end/ElementsTreeOutline.js:

(WebInspector.ElementsTreeOutline):
(WebInspector.ElementsTreeOutline.prototype._createNodeDecorators):
(WebInspector.ElementsTreeOutline.prototype.updateOpenCloseTags):
(WebInspector.ElementsTreeOutline.ElementDecorator):
(WebInspector.ElementsTreeOutline.ElementDecorator.prototype.decorate):
(WebInspector.ElementsTreeOutline.ElementDecorator.prototype.decorateAncestor):
(WebInspector.ElementsTreeOutline.PseudoStateDecorator):
(WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype.decorate):
(WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype.decorateAncestor):
(WebInspector.ElementsTreeElement.prototype._populateTagContextMenu):
(WebInspector.ElementsTreeElement.prototype._populateForcedPseudoStateItems):
(WebInspector.ElementsTreeElement.prototype.updateTitle):
(WebInspector.ElementsTreeElement.prototype._createDecoratorElement):
(WebInspector.ElementsTreeElement.prototype._updateDecorations):

  • inspector/front-end/StylesSidebarPane.js:

(WebInspector.StylesSidebarPane):
(WebInspector.StylesSidebarPane.prototype.get forcedPseudoClasses):
(WebInspector.StylesSidebarPane.prototype._updateForcedPseudoStateInputs):
(WebInspector.StylesSidebarPane.prototype.update):
(WebInspector.StylesSidebarPane.prototype._refreshUpdate):
(WebInspector.StylesSidebarPane.prototype._rebuildUpdate):
(WebInspector.StylesSidebarPane.prototype._toggleElementStatePane):
(WebInspector.StylesSidebarPane.prototype._createElementStatePane.clickListener):

  • inspector/front-end/elementsPanel.css:

(#elements-content .elements-gutter-decoration):
(#elements-content .elements-gutter-decoration.elements-has-decorated-children):

LayoutTests:

  • inspector/styles/force-pseudo-state-expected.txt: Added.
  • inspector/styles/force-pseudo-state.html: Added.
Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r121842 r121860  
     12012-07-04  Alexander Pavlov  <apavlov@chromium.org>
     2
     3        Web Inspector: Emulate pseudo styles (hover etc.) of non-selected elements
     4        https://bugs.webkit.org/show_bug.cgi?id=86630
     5
     6        Reviewed by Pavel Feldman.
     7
     8        * inspector/styles/force-pseudo-state-expected.txt: Added.
     9        * inspector/styles/force-pseudo-state.html: Added.
     10
    1112012-07-04  Vsevolod Vlasov  <vsevik@chromium.org>
    212
  • trunk/Source/WebCore/ChangeLog

    r121859 r121860  
     12012-07-03  Alexander Pavlov  <apavlov@chromium.org>
     2
     3        Web Inspector: Emulate pseudo styles (hover etc.) of non-selected elements
     4        https://bugs.webkit.org/show_bug.cgi?id=86630
     5
     6        Reviewed by Pavel Feldman.
     7
     8        - A map of pseudo-states for all bound DOM nodes is maintained in the backend and queried whenever StyleResolver
     9        calculates the effective element style.
     10        - In the frontend, markers are introduced to distinguish elements that have forced pseudo styles set for them.
     11        Additionally, dimmed markers are added for collapsed nodes, whose descendants have forced pseudo styles.
     12        More ElementDecorator subtypes will be added for other types of markers.
     13
     14        Test: inspector/styles/force-pseudo-state.html
     15
     16        * English.lproj/localizedStrings.js:
     17        * inspector/InspectorCSSAgent.cpp:
     18        (WebCore::InspectorCSSAgent::InspectorCSSAgent):
     19        (WebCore::InspectorCSSAgent::clearFrontend):
     20        (WebCore::InspectorCSSAgent::reset):
     21        (WebCore::InspectorCSSAgent::forcePseudoState):
     22        (WebCore::InspectorCSSAgent::recalcStyleForPseudoStateIfNeeded):
     23        (WebCore::InspectorCSSAgent::elementForId):
     24        (WebCore::InspectorCSSAgent::didRemoveDocument):
     25        (WebCore::InspectorCSSAgent::didRemoveDOMNode):
     26        (WebCore::InspectorCSSAgent::resetPseudoStates):
     27        * inspector/InspectorCSSAgent.h:
     28        (InspectorCSSAgent):
     29        * inspector/InspectorDOMAgent.cpp:
     30        (WebCore::InspectorDOMAgent::unbind):
     31        (WebCore::InspectorDOMAgent::didRemoveDOMNode):
     32        * inspector/front-end/ElementsPanel.js:
     33        (WebInspector.ElementsPanel.get this):
     34        (WebInspector.ElementsPanel):
     35        (WebInspector.ElementsPanel.prototype._setPseudoClassForNodeId):
     36        * inspector/front-end/ElementsTreeOutline.js:
     37        (WebInspector.ElementsTreeOutline):
     38        (WebInspector.ElementsTreeOutline.prototype._createNodeDecorators):
     39        (WebInspector.ElementsTreeOutline.prototype.updateOpenCloseTags):
     40        (WebInspector.ElementsTreeOutline.ElementDecorator):
     41        (WebInspector.ElementsTreeOutline.ElementDecorator.prototype.decorate):
     42        (WebInspector.ElementsTreeOutline.ElementDecorator.prototype.decorateAncestor):
     43        (WebInspector.ElementsTreeOutline.PseudoStateDecorator):
     44        (WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype.decorate):
     45        (WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype.decorateAncestor):
     46        (WebInspector.ElementsTreeElement.prototype._populateTagContextMenu):
     47        (WebInspector.ElementsTreeElement.prototype._populateForcedPseudoStateItems):
     48        (WebInspector.ElementsTreeElement.prototype.updateTitle):
     49        (WebInspector.ElementsTreeElement.prototype._createDecoratorElement):
     50        (WebInspector.ElementsTreeElement.prototype._updateDecorations):
     51        * inspector/front-end/StylesSidebarPane.js:
     52        (WebInspector.StylesSidebarPane):
     53        (WebInspector.StylesSidebarPane.prototype.get forcedPseudoClasses):
     54        (WebInspector.StylesSidebarPane.prototype._updateForcedPseudoStateInputs):
     55        (WebInspector.StylesSidebarPane.prototype.update):
     56        (WebInspector.StylesSidebarPane.prototype._refreshUpdate):
     57        (WebInspector.StylesSidebarPane.prototype._rebuildUpdate):
     58        (WebInspector.StylesSidebarPane.prototype._toggleElementStatePane):
     59        (WebInspector.StylesSidebarPane.prototype._createElementStatePane.clickListener):
     60        * inspector/front-end/elementsPanel.css:
     61        (#elements-content .elements-gutter-decoration):
     62        (#elements-content .elements-gutter-decoration.elements-has-decorated-children):
     63
    1642012-07-04  Pavel Feldman  <pfeldman@chromium.org>
    265
  • trunk/Source/WebCore/English.lproj/localizedStrings.js

    r121763 r121860  
    1818localizedStrings["%d console messages are not shown."] = "%d console messages are not shown.";
    1919localizedStrings["%d cookies (%s)"] = "%d cookies (%s)";
     20localizedStrings["%d descendant with forced state"] = "%d descendant with forced state";
     21localizedStrings["%d descendants with forced state"] = "%d descendants with forced state";
    2022localizedStrings["%d error"] = "%d error";
    2123localizedStrings["%d error, %d warning"] = "%d error, %d warning";
     
    182184localizedStrings["Edit as HTML"] = "Edit as HTML";
    183185localizedStrings["Edit"] = "Edit";
     186localizedStrings["Element state: %s"] = "Element state: %s";
    184187localizedStrings["Elements Panel"] = "Elements Panel";
    185188localizedStrings["Elements"] = "Elements";
     
    203206localizedStrings["File size"] = "File size";
    204207localizedStrings["Fit in window"] = "Fit in window";
     208localizedStrings["Force Element State"] = "Force Element State";
     209localizedStrings["Force element state"] = "Force element state";
    205210localizedStrings["Go to the panel to the left/right"] = "Go to the panel to the left/right";
    206211localizedStrings["Go back/forward in panel history"] = "Go back/forward in panel history";
  • trunk/Source/WebCore/inspector/InspectorCSSAgent.cpp

    r120044 r121860  
    462462    , m_frontend(0)
    463463    , m_domAgent(domAgent)
    464     , m_lastPseudoState(0)
    465464    , m_lastStyleSheetId(1)
    466465{
     
    486485    ASSERT(m_frontend);
    487486    m_frontend = 0;
    488     clearPseudoState(true);
     487    resetPseudoStates();
    489488    String errorString;
    490489    stopSelectorProfilerImpl(&errorString, false);
     
    515514    m_nodeToInspectorStyleSheet.clear();
    516515    m_documentToInspectorStyleSheet.clear();
     516    resetPseudoStates();
    517517}
    518518
     
    535535bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
    536536{
    537     if (m_lastElementWithPseudoState != element)
     537    if (m_nodeIdToForcedPseudoState.isEmpty())
    538538        return false;
    539539
     540    int nodeId = m_domAgent->boundNodeId(element);
     541    if (!nodeId)
     542        return false;
     543
     544    NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
     545    if (it == m_nodeIdToForcedPseudoState.end())
     546        return false;
     547
     548    unsigned forcedPseudoState = it->second;
    540549    switch (pseudoType) {
    541550    case CSSSelector::PseudoActive:
    542         return m_lastPseudoState & PseudoActive;
     551        return forcedPseudoState & PseudoActive;
    543552    case CSSSelector::PseudoFocus:
    544         return m_lastPseudoState & PseudoFocus;
     553        return forcedPseudoState & PseudoFocus;
    545554    case CSSSelector::PseudoHover:
    546         return m_lastPseudoState & PseudoHover;
     555        return forcedPseudoState & PseudoHover;
    547556    case CSSSelector::PseudoVisited:
    548         return m_lastPseudoState & PseudoVisited;
     557        return forcedPseudoState & PseudoVisited;
    549558    default:
    550559        return false;
     
    554563void InspectorCSSAgent::recalcStyleForPseudoStateIfNeeded(Element* element, InspectorArray* forcedPseudoClasses)
    555564{
    556     unsigned forcePseudoState = computePseudoClassMask(forcedPseudoClasses);
    557     bool needStyleRecalc = element != m_lastElementWithPseudoState || forcePseudoState != m_lastPseudoState;
    558     m_lastPseudoState = forcePseudoState;
    559     m_lastElementWithPseudoState = element;
    560     if (needStyleRecalc)
    561         element->ownerDocument()->styleResolverChanged(RecalcStyleImmediately);
     565    int nodeId = m_domAgent->boundNodeId(element);
     566    if (!nodeId)
     567        return;
     568
     569    unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses);
     570    NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
     571    unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->second;
     572    bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState;
     573    if (!needStyleRecalc)
     574        return;
     575
     576    if (forcedPseudoState)
     577        m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState);
     578    else
     579        m_nodeIdToForcedPseudoState.remove(nodeId);
     580    element->ownerDocument()->styleResolverChanged(RecalcStyleImmediately);
    562581}
    563582
     
    845864        return 0;
    846865    }
    847     return static_cast<Element*>(node);
     866    return toElement(node);
    848867}
    849868
     
    9861005    if (document)
    9871006        m_documentToInspectorStyleSheet.remove(document);
    988     clearPseudoState(false);
    9891007}
    9901008
     
    9941012        return;
    9951013
    996     if (m_lastElementWithPseudoState.get() == node)
    997         clearPseudoState(false);
     1014    int nodeId = m_domAgent->boundNodeId(node);
     1015    if (nodeId)
     1016        m_nodeIdToForcedPseudoState.remove(nodeId);
    9981017
    9991018    NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
     
    10231042}
    10241043
    1025 void InspectorCSSAgent::clearPseudoState(bool recalcStyles)
    1026 {
    1027     RefPtr<Element> element = m_lastElementWithPseudoState;
    1028     m_lastElementWithPseudoState = 0;
    1029     m_lastPseudoState = 0;
    1030     if (recalcStyles && element) {
    1031         Document* document = element->ownerDocument();
    1032         if (document)
    1033             document->styleResolverChanged(RecalcStyleImmediately);
    1034     }
     1044void InspectorCSSAgent::resetPseudoStates()
     1045{
     1046    HashSet<Document*> documentsToChange;
     1047    for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) {
     1048        Element* element = toElement(m_domAgent->nodeForId(it->first));
     1049        if (element && element->ownerDocument())
     1050            documentsToChange.add(element->ownerDocument());
     1051    }
     1052
     1053    m_nodeIdToForcedPseudoState.clear();
     1054    for (HashSet<Document*>::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it)
     1055        (*it)->styleResolverChanged(RecalcStyleImmediately);
    10351056}
    10361057
  • trunk/Source/WebCore/inspector/InspectorCSSAgent.h

    r120044 r121860  
    135135    typedef HashMap<Node*, RefPtr<InspectorStyleSheetForInlineStyle> > NodeToInspectorStyleSheet; // bogus "stylesheets" with elements' inline styles
    136136    typedef HashMap<RefPtr<Document>, RefPtr<InspectorStyleSheet> > DocumentToViaInspectorStyleSheet; // "via inspector" stylesheets
     137    typedef HashMap<int, unsigned> NodeIdToForcedPseudoState;
    137138
    138139    void recalcStyleForPseudoStateIfNeeded(Element*, InspectorArray* forcedPseudoClasses);
     
    157158    virtual void styleSheetChanged(InspectorStyleSheet*);
    158159
    159     void clearPseudoState(bool recalcStyles);
     160    void resetPseudoStates();
    160161
    161162    InspectorFrontend::CSS* m_frontend;
     
    166167    NodeToInspectorStyleSheet m_nodeToInspectorStyleSheet;
    167168    DocumentToViaInspectorStyleSheet m_documentToInspectorStyleSheet;
    168 
    169     RefPtr<Element> m_lastElementWithPseudoState;
    170     unsigned m_lastPseudoState;
     169    NodeIdToForcedPseudoState m_nodeIdToForcedPseudoState;
    171170
    172171    int m_lastStyleSheetId;
  • trunk/Source/WebCore/inspector/InspectorDOMAgent.cpp

    r119396 r121860  
    351351
    352352    nodesMap->remove(node);
     353    if (m_domListener)
     354        m_domListener->didRemoveDOMNode(node);
     355
    353356    bool childrenRequested = m_childrenRequested.contains(id);
    354357    if (childrenRequested) {
     
    14441447    int parentId = m_documentNodeToIdMap.get(parent);
    14451448
    1446     if (m_domListener)
    1447         m_domListener->didRemoveDOMNode(node);
    1448 
    14491449    if (!m_childrenRequested.contains(parentId)) {
    14501450        // No children are mapped yet -> only notify on changes of hasChildren.
  • trunk/Source/WebCore/inspector/front-end/ElementsPanel.js

    r120711 r121860  
    5656    this.contentElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
    5757
    58     this.treeOutline = new WebInspector.ElementsTreeOutline(true, true, false, this._populateContextMenu.bind(this));
     58    this.treeOutline = new WebInspector.ElementsTreeOutline(true, true, false, this._populateContextMenu.bind(this), this._setPseudoClassForNodeId.bind(this));
    5959    this.treeOutline.wireToDomAgent();
    6060
     
    6868    this.sidebarPanes = {};
    6969    this.sidebarPanes.computedStyle = new WebInspector.ComputedStyleSidebarPane();
    70     this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle);
     70    this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle, this._setPseudoClassForNodeId.bind(this));
    7171    this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
    7272    this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
     
    165165        this.treeOutline.updateSelection();
    166166        this.updateBreadcrumbSizes();
     167    },
     168
     169    /**
     170     * @param {DOMAgent.NodeId} nodeId
     171     * @param {string} pseudoClass
     172     * @param {boolean} enable
     173     */
     174    _setPseudoClassForNodeId: function(nodeId, pseudoClass, enable)
     175    {
     176        var node = WebInspector.domAgent.nodeForId(nodeId);
     177        if (!node)
     178            return;
     179
     180        var pseudoClasses = node.getUserProperty("pseudoState");
     181        if (enable) {
     182            pseudoClasses = pseudoClasses || [];
     183            if (pseudoClasses.indexOf(pseudoClass) >= 0)
     184                return;
     185            pseudoClasses.push(pseudoClass);
     186            node.setUserProperty("pseudoState", pseudoClasses);
     187        } else {
     188            if (!pseudoClasses || pseudoClasses.indexOf(pseudoClass) < 0)
     189                return;
     190            pseudoClasses.remove(pseudoClass);
     191            if (!pseudoClasses.length)
     192                node.removeUserProperty("pseudoState");
     193        }
     194
     195        this.treeOutline.updateOpenCloseTags(node);
     196        this._metricsPaneEdited();
     197        this._stylesPaneEdited();
    167198    },
    168199
  • trunk/Source/WebCore/inspector/front-end/ElementsTreeOutline.js

    r121751 r121860  
    3636 * @param {boolean=} showInElementsPanelEnabled
    3737 * @param {function(WebInspector.ContextMenu, WebInspector.DOMNode)=} contextMenuCallback
     38 * @param {function(DOMAgent.NodeId, string, boolean)=} setPseudoClassCallback
    3839 */
    39 WebInspector.ElementsTreeOutline = function(omitRootDOMNode, selectEnabled, showInElementsPanelEnabled, contextMenuCallback)
     40WebInspector.ElementsTreeOutline = function(omitRootDOMNode, selectEnabled, showInElementsPanelEnabled, contextMenuCallback, setPseudoClassCallback)
    4041{
    4142    this.element = document.createElement("ol");
     
    6364    this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
    6465    this._contextMenuCallback = contextMenuCallback;
     66    this._setPseudoClassCallback = setPseudoClassCallback;
     67    this._createNodeDecorators();
    6568}
    6669
     
    7073
    7174WebInspector.ElementsTreeOutline.prototype = {
     75    _createNodeDecorators: function()
     76    {
     77        this._nodeDecorators = [];
     78        this._nodeDecorators.push(new WebInspector.ElementsTreeOutline.PseudoStateDecorator());
     79    },
     80
    7281    wireToDomAgent: function()
    7382    {
     
    183192    },
    184193
     194    /**
     195     * @param {WebInspector.DOMNode} node
     196     */
     197    updateOpenCloseTags: function(node)
     198    {
     199        var treeElement = this.findTreeElement(node);
     200        if (treeElement)
     201            treeElement.updateTitle();
     202        var children = treeElement.children;
     203        var closingTagElement = children[children.length - 1];
     204        if (closingTagElement && closingTagElement._elementCloseTag)
     205            closingTagElement.updateTitle();
     206    },
     207
    185208    _selectedNodeChanged: function()
    186209    {
     
    188211    },
    189212
     213    /**
     214     * @param {WebInspector.DOMNode} node
     215     */
    190216    findTreeElement: function(node)
    191217    {
     
    209235    },
    210236
     237    /**
     238     * @param {WebInspector.DOMNode} node
     239     */
    211240    createTreeElementFor: function(node)
    212241    {
     
    555584
    556585/**
     586 * @interface
     587 */
     588WebInspector.ElementsTreeOutline.ElementDecorator = function()
     589{
     590}
     591
     592WebInspector.ElementsTreeOutline.ElementDecorator.prototype = {
     593    /**
     594     * @param {WebInspector.DOMNode} node
     595     */
     596    decorate: function(node)
     597    {
     598    },
     599
     600    /**
     601     * @param {WebInspector.DOMNode} node
     602     */
     603    decorateAncestor: function(node)
     604    {
     605    }
     606}
     607
     608/**
     609 * @constructor
     610 * @implements {WebInspector.ElementsTreeOutline.ElementDecorator}
     611 */
     612WebInspector.ElementsTreeOutline.PseudoStateDecorator = function()
     613{
     614    WebInspector.ElementsTreeOutline.ElementDecorator.call(this);
     615}
     616
     617WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName = "pseudoState";
     618
     619WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype = {
     620    decorate: function(node)
     621    {
     622        if (node.nodeType() !== Node.ELEMENT_NODE)
     623            return null;
     624        var propertyValue = node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
     625        if (!propertyValue)
     626            return null;
     627        return WebInspector.UIString("Element state: %s", ":" + propertyValue.join(", :"));
     628    },
     629
     630    decorateAncestor: function(node)
     631    {
     632        if (node.nodeType() !== Node.ELEMENT_NODE)
     633            return null;
     634
     635        var descendantCount = node.descendantUserPropertyCount(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
     636        if (!descendantCount)
     637            return null;
     638        if (descendantCount === 1)
     639            return WebInspector.UIString("%d descendant with forced state", descendantCount);
     640        return WebInspector.UIString("%d descendants with forced state", descendantCount);
     641    }
     642}
     643
     644WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype.__proto__ = WebInspector.ElementsTreeOutline.ElementDecorator.prototype;
     645
     646/**
    557647 * @constructor
    558648 * @extends {TreeElement}
     
    10651155            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit attribute" : "Edit Attribute"), this._startEditingAttribute.bind(this, attribute, event.target));
    10661156        contextMenu.appendSeparator();
     1157        if (this.treeOutline._setPseudoClassCallback) {
     1158            var pseudoSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Force element state" : "Force Element State"));
     1159            this._populateForcedPseudoStateItems(pseudoSubMenu);
     1160            contextMenu.appendSeparator();
     1161        }
    10671162
    10681163        this._populateNodeContextMenu(contextMenu);
    10691164        this.treeOutline._populateContextMenu(contextMenu, this.representedObject);
     1165    },
     1166
     1167    _populateForcedPseudoStateItems: function(subMenu)
     1168    {
     1169        const pseudoClasses = ["active", "hover", "focus", "visited"];
     1170        var node = this.representedObject;
     1171        var forcedPseudoState = (node ? node.getUserProperty("pseudoState") : null) || [];
     1172        var elementsPanel = WebInspector.panels.elements;
     1173        for (var i = 0; i < pseudoClasses.length; ++i) {
     1174            var pseudoClassForced = forcedPseudoState.indexOf(pseudoClasses[i]) >= 0;
     1175            subMenu.appendCheckboxItem(":" + pseudoClasses[i], this.treeOutline._setPseudoClassCallback.bind(null, node.id, pseudoClasses[i], !pseudoClassForced), pseudoClassForced, false);
     1176        }
    10701177    },
    10711178
     
    14701577            highlightElement.appendChild(this._nodeTitleInfo(WebInspector.linkifyURLAsNode).titleDOM);
    14711578            this.title = highlightElement;
     1579            this._updateDecorations();
    14721580            delete this._highlightResult;
    14731581        }
     
    14781586        this._preventFollowingLinksOnDoubleClick();
    14791587        this._highlightSearchResults();
     1588    },
     1589
     1590    _createDecoratorElement: function()
     1591    {
     1592        var node = this.representedObject;
     1593        var decoratorMessages = [];
     1594        var parentDecoratorMessages = [];
     1595        for (var i = 0; i < this.treeOutline._nodeDecorators.length; ++i) {
     1596            var decorator = this.treeOutline._nodeDecorators[i];
     1597            var message = decorator.decorate(node);
     1598            if (message) {
     1599                decoratorMessages.push(message);
     1600                continue;
     1601            }
     1602
     1603            if (this.expanded || this._elementCloseTag)
     1604                continue;
     1605
     1606            message = decorator.decorateAncestor(node);
     1607            if (message)
     1608                parentDecoratorMessages.push(message)
     1609        }
     1610        if (!decoratorMessages.length && !parentDecoratorMessages.length)
     1611            return null;
     1612
     1613        var decoratorElement = document.createElement("div");
     1614        decoratorElement.addStyleClass("elements-gutter-decoration");
     1615        if (!decoratorMessages.length)
     1616            decoratorElement.addStyleClass("elements-has-decorated-children");
     1617        decoratorElement.title = decoratorMessages.concat(parentDecoratorMessages).join("\n");
     1618        return decoratorElement;
     1619    },
     1620
     1621    _updateDecorations: function()
     1622    {
     1623        if (this._decoratorElement && this._decoratorElement.parentElement)
     1624            this._decoratorElement.parentElement.removeChild(this._decoratorElement);
     1625        this._decoratorElement = this._createDecoratorElement();
     1626        if (this._decoratorElement && this.listItemElement)
     1627            this.listItemElement.insertBefore(this._decoratorElement, this.listItemElement.firstChild);
    14801628    },
    14811629
  • trunk/Source/WebCore/inspector/front-end/StylesSidebarPane.js

    r121156 r121860  
    3131 * @constructor
    3232 * @extends {WebInspector.SidebarPane}
     33 * @param {WebInspector.ComputedStyleSidebarPane} computedStylePane
     34 * @param {function(DOMAgent.NodeId, string, boolean)} setPseudoClassCallback
    3335 */
    34 WebInspector.StylesSidebarPane = function(computedStylePane)
     36WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallback)
    3537{
    3638    WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
     
    8385    this._computedStylePane = computedStylePane;
    8486    computedStylePane._stylesSidebarPane = this;
     87    this._setPseudoClassCallback = setPseudoClassCallback;
    8588    this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
    8689    WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this));
     
    212215    get forcedPseudoClasses()
    213216    {
    214         return this._forcedPseudoClasses;
     217        return this.node ? (this.node.getUserProperty("pseudoState") || undefined) : undefined;
     218    },
     219
     220    _updateForcedPseudoStateInputs: function()
     221    {
     222        if (!this.node)
     223            return;
     224
     225        var nodePseudoState = this.forcedPseudoClasses;
     226        if (!nodePseudoState)
     227            nodePseudoState = [];
     228
     229        var inputs = this._elementStatePane.inputs;
     230        for (var i = 0; i < inputs.length; ++i)
     231            inputs[i].checked = nodePseudoState.indexOf(inputs[i].state) >= 0;
    215232    },
    216233
     
    238255        else
    239256            node = this.node;
     257
     258        this._updateForcedPseudoStateInputs();
    240259
    241260        if (refresh)
     
    280299        if (this._computedStylePane.expanded || forceFetchComputedStyle) {
    281300            this._refreshUpdateInProgress = true;
    282             WebInspector.cssModel.getComputedStyleAsync(node.id, this._forcedPseudoClasses, computedStyleCallback.bind(this));
     301            WebInspector.cssModel.getComputedStyleAsync(node.id, this.forcedPseudoClasses, computedStyleCallback.bind(this));
    283302        } else {
    284303            this._innerRefreshUpdate(node, null, editedSection);
     
    338357
    339358        if (this._computedStylePane.expanded)
    340             WebInspector.cssModel.getComputedStyleAsync(node.id, this._forcedPseudoClasses, computedCallback.bind(this));
     359            WebInspector.cssModel.getComputedStyleAsync(node.id, this.forcedPseudoClasses, computedCallback.bind(this));
    341360        WebInspector.cssModel.getInlineStylesAsync(node.id, inlineCallback.bind(this));
    342         WebInspector.cssModel.getMatchedStylesAsync(node.id, this._forcedPseudoClasses, true, true, stylesCallback.bind(this));
    343     },
    344 
     361        WebInspector.cssModel.getMatchedStylesAsync(node.id, this.forcedPseudoClasses, true, true, stylesCallback.bind(this));
     362    },
     363
     364    /**
     365     * @param {function()=} userCallback
     366     */
    345367    _validateNode: function(userCallback)
    346368    {
     
    814836            this._elementStateButton.removeStyleClass("toggled");
    815837            this._elementStatePane.removeStyleClass("expanded");
    816             // Clear flags on hide.
    817             if (this._forcedPseudoClasses) {
    818                 for (var i = 0; i < this._elementStatePane.inputs.length; ++i)
    819                     this._elementStatePane.inputs[i].checked = false;
    820                 delete this._forcedPseudoClasses;
    821                 this._rebuildUpdate();
    822             }
    823838        }
    824839    },
     
    835850        function clickListener(event)
    836851        {
    837             var pseudoClasses = [];
    838             for (var i = 0; i < inputs.length; ++i) {
    839                 if (inputs[i].checked)
    840                     pseudoClasses.push(inputs[i].state);
    841             }
    842             this._forcedPseudoClasses = pseudoClasses.length ? pseudoClasses : undefined;
    843             this._rebuildUpdate();
     852            var node = this._validateNode();
     853            if (!node)
     854                return;
     855            this._setPseudoClassCallback(node.id, event.target.state, event.target.checked);
    844856        }
    845857
  • trunk/Source/WebCore/inspector/front-end/elementsPanel.css

    r121150 r121860  
    4040}
    4141
     42#elements-content .elements-gutter-decoration {
     43    position: absolute;
     44    left: 1px;
     45    margin-top: 2px;
     46    height: 8px;
     47    width: 8px;
     48    border-radius: 4px;
     49    border: 1px solid orange;
     50    background-color: orange;
     51}
     52
     53#elements-content .elements-gutter-decoration.elements-has-decorated-children {
     54    opacity: 0.5;
     55}
     56
    4257.elements-tree-editor {
    4358    -webkit-user-select: text;
Note: See TracChangeset for help on using the changeset viewer.