Title: [193441] branches/safari-601.1.46-branch
Revision
193441
Author
[email protected]
Date
2015-12-04 12:51:49 -0800 (Fri, 04 Dec 2015)

Log Message

Merge r187496. rdar://problem/23581597

Modified Paths

Added Paths

Diff

Modified: branches/safari-601.1.46-branch/LayoutTests/ChangeLog (193440 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/ChangeLog	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/LayoutTests/ChangeLog	2015-12-04 20:51:49 UTC (rev 193441)
@@ -1,5 +1,23 @@
 2015-12-04  Timothy Hatcher  <[email protected]>
 
+        Merge r187496. rdar://problem/23581597
+
+    2015-07-28  Joseph Pecoraro  <[email protected]>
+
+            Web Inspector: Show Pseudo Elements in DOM Tree
+            https://bugs.webkit.org/show_bug.cgi?id=139612
+
+            Reviewed by Timothy Hatcher.
+
+            * inspector/css/pseudo-element-matches-for-pseudo-element-node-expected.txt: Added.
+            * inspector/css/pseudo-element-matches-for-pseudo-element-node.html: Added.
+            * inspector/dom/pseudo-element-dynamic-expected.txt: Added.
+            * inspector/dom/pseudo-element-dynamic.html: Added.
+            * inspector/dom/pseudo-element-static-expected.txt: Added.
+            * inspector/dom/pseudo-element-static.html: Added.
+
+2015-12-04  Timothy Hatcher  <[email protected]>
+
         Merge r187249. rdar://problem/23581597
 
     2015-07-23  Devin Rousso  <[email protected]>

Added: branches/safari-601.1.46-branch/LayoutTests/inspector/css/pseudo-element-matches-for-pseudo-element-node-expected.txt (0 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/inspector/css/pseudo-element-matches-for-pseudo-element-node-expected.txt	                        (rev 0)
+++ branches/safari-601.1.46-branch/LayoutTests/inspector/css/pseudo-element-matches-for-pseudo-element-node-expected.txt	2015-12-04 20:51:49 UTC (rev 193441)
@@ -0,0 +1,18 @@
+Testing that we can get computed and matched rules for pseudo element nodes.
+
+TEST ELEMENT: Has Before Pseudo Element
+PASS: Got ::before pseudo element for #x
+Refreshing styles for ::before pseudo element
+
+PASS: No Inline Styles
+PASS: No Attribute Styles
+PASS: No Inherited Styles
+PASS: No Pseudo Element Styles
+PASS: Has Computed Styles
+PROPERTY: content: before;
+PROPERTY: color: rgb(0, 0, 255);
+MATCHED RULE:
+  SELECTOR: input:before
+  SELECTOR: #x:before [matched]
+  SELECTOR: div
+

Added: branches/safari-601.1.46-branch/LayoutTests/inspector/css/pseudo-element-matches-for-pseudo-element-node.html (0 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/inspector/css/pseudo-element-matches-for-pseudo-element-node.html	                        (rev 0)
+++ branches/safari-601.1.46-branch/LayoutTests/inspector/css/pseudo-element-matches-for-pseudo-element-node.html	2015-12-04 20:51:49 UTC (rev 193441)
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function test() {
+    var nodeStyles = null;
+
+    function validateStyles() {
+        InspectorTest.log("");
+
+        InspectorTest.expectThat(!nodeStyles.inlineStyle, "No Inline Styles");
+        InspectorTest.expectThat(!nodeStyles.attributesStyle, "No Attribute Styles");
+        InspectorTest.expectThat(!nodeStyles.inheritedRules.length, "No Inherited Styles");
+        InspectorTest.expectThat(!Object.keys(nodeStyles.pseudoElements).length, "No Pseudo Element Styles");
+
+        InspectorTest.expectThat(nodeStyles.computedStyle, "Has Computed Styles");
+        InspectorTest.log("PROPERTY: " + nodeStyles.computedStyle.propertyForName("content").text);
+        InspectorTest.log("PROPERTY: " + nodeStyles.computedStyle.propertyForName("color").text);
+
+        for (var rule of nodeStyles.matchedRules) {
+            InspectorTest.log("MATCHED RULE:");
+            for (var i = 0; i < rule.selectors.length; ++i) {
+                var selector = rule.selectors[i];
+                var matched = rule.matchedSelectorIndices.includes(i);
+                InspectorTest.log("  SELECTOR: " + selector.text + (matched ? " [matched]" : ""));
+            }
+        }
+
+        InspectorTest.completeTest();                
+    }
+
+    WebInspector.domTreeManager.requestDocument(function(documentNode) {
+        WebInspector.domTreeManager.querySelector(documentNode.id, "#x", function(contentNodeId) {
+            var domNode = WebInspector.domTreeManager.nodeForId(contentNodeId);
+            InspectorTest.assert(domNode);
+            var beforePseudoElementDOMNode = domNode.beforePseudoElement();
+            InspectorTest.expectThat(beforePseudoElementDOMNode, "Got ::before pseudo element for #x");
+            InspectorTest.log("Refreshing styles for ::before pseudo element");
+            nodeStyles = WebInspector.cssStyleManager.stylesForNode(beforePseudoElementDOMNode);
+            nodeStyles.addEventListener(WebInspector.DOMNodeStyles.Event.Refreshed, validateStyles);
+            nodeStyles.refresh();
+        });
+    });
+
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <p>Testing that we can get computed and matched rules for pseudo element nodes.</p>
+
+    <style>
+    input:before, #x:before, div { content: "before"; color: blue; }
+    </style>
+
+    <div id="x">TEST ELEMENT: Has Before Pseudo Element</div>
+</body>
+</html>

Added: branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-dynamic-expected.txt (0 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-dynamic-expected.txt	                        (rev 0)
+++ branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-dynamic-expected.txt	2015-12-04 20:51:49 UTC (rev 193441)
@@ -0,0 +1,49 @@
+Test for dynamic DOMNode pseudo element support, pseudo elements getting added and removed dynamically.
+
+TEST ELEMENT
+
+PASS: Got DOMNode for #x
+PASS: DOMNode has no pseudo elements
+
+Add :before
+EVENT: NodeInserted
+
+Verify :before was added
+PASS: #x DOMNode has pseudo elements
+PASS: DOMNode has 1 pseudo elements
+PASS: DOMNode has a before pseudo element
+PASS: DOMNode does not have an after pseudo element
+PASS: New DOMNode is the beforePseudoElement
+PASS: New DOMNode is a child of #x
+PASS: New DOMNode is a before pseudo element
+
+Remove :before
+EVENT: NodeRemoved
+
+Verify :before was removed
+PASS: #x DOMNode has no pseudo elements
+PASS: Removed DOMNode was the before pseudo element
+PASS: Removed DOMNode has no parent
+PASS: Removed DOMNode was a before pseudo element
+
+Add :after
+EVENT: NodeInserted
+
+Verify :after was added
+PASS: #x DOMNode has pseudo elements
+PASS: DOMNode has 1 pseudo elements
+PASS: DOMNode does not have a before pseudo element
+PASS: DOMNode has an after pseudo element
+PASS: New DOMNode is the afterPseudoElement
+PASS: New DOMNode is a child of #x
+PASS: New DOMNode is an after pseudo element
+
+Remove :after
+EVENT: NodeRemoved
+
+Verify :after was removed
+PASS: #x DOMNode has no pseudo elements
+PASS: Removed DOMNode was the after pseudo element
+PASS: Removed DOMNode has no parent
+PASS: Removed DOMNode was an after pseudo element
+

Added: branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-dynamic.html (0 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-dynamic.html	                        (rev 0)
+++ branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-dynamic.html	2015-12-04 20:51:49 UTC (rev 193441)
@@ -0,0 +1,156 @@
+<html>
+<head>
+<script src=""
+<script>
+function test() {
+
+    var testDOMNode = null;
+    var insertedOrRemovedDOMNode = null;
+    var previousInsertedDOMNode = null;
+
+    var steps = [
+        {
+            name: "Add :before",
+            action: function() {
+                InspectorTest.evaluateInPage("document.getElementById('x').classList.add('b')");
+            }
+        },
+        {
+            name: "Verify :before was added",
+            action: function() {
+                InspectorTest.expectThat(testDOMNode.hasPseudoElements(), "#x DOMNode has pseudo elements");
+                InspectorTest.expectThat(testDOMNode.pseudoElements().size === 1, "DOMNode has 1 pseudo elements");
+                InspectorTest.expectThat(testDOMNode.beforePseudoElement(), "DOMNode has a before pseudo element");
+                InspectorTest.expectThat(!testDOMNode.afterPseudoElement(), "DOMNode does not have an after pseudo element");
+
+                InspectorTest.expectThat(insertedOrRemovedDOMNode === testDOMNode.beforePseudoElement(), "New DOMNode is the beforePseudoElement");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.parentNode === testDOMNode, "New DOMNode is a child of #x");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.pseudoType() === WebInspector.DOMNode.PseudoElementType.Before, "New DOMNode is a before pseudo element");
+
+                previousInsertedDOMNode = insertedOrRemovedDOMNode;
+                next();
+            }
+        },
+        {
+            name: "Remove :before",
+            action: function() {
+                InspectorTest.evaluateInPage("document.getElementById('x').classList.remove('b')");
+            }
+        },
+        {
+            name: "Verify :before was removed",
+            action: function() {
+                InspectorTest.expectThat(!testDOMNode.hasPseudoElements(), "#x DOMNode has no pseudo elements");
+                InspectorTest.assert(testDOMNode.pseudoElements().size === 0);
+                InspectorTest.assert(!testDOMNode.beforePseudoElement());
+                InspectorTest.assert(!testDOMNode.afterPseudoElement());
+
+                InspectorTest.expectThat(insertedOrRemovedDOMNode === previousInsertedDOMNode, "Removed DOMNode was the before pseudo element");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.parentNode === null, "Removed DOMNode has no parent");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.pseudoType() === WebInspector.DOMNode.PseudoElementType.Before, "Removed DOMNode was a before pseudo element");
+
+                previousInsertedDOMNode = null;
+                next();
+            }
+        },
+        {
+            name: "Add :after",
+            action: function() {
+                InspectorTest.evaluateInPage("document.getElementById('x').classList.add('a')");
+            }
+        },
+        {
+            name: "Verify :after was added",
+            action: function() {
+                InspectorTest.expectThat(testDOMNode.hasPseudoElements(), "#x DOMNode has pseudo elements");
+                InspectorTest.expectThat(testDOMNode.pseudoElements().size === 1, "DOMNode has 1 pseudo elements");
+                InspectorTest.expectThat(!testDOMNode.beforePseudoElement(), "DOMNode does not have a before pseudo element");
+                InspectorTest.expectThat(testDOMNode.afterPseudoElement(), "DOMNode has an after pseudo element");
+
+                InspectorTest.expectThat(insertedOrRemovedDOMNode === testDOMNode.afterPseudoElement(), "New DOMNode is the afterPseudoElement");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.parentNode === testDOMNode, "New DOMNode is a child of #x");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.pseudoType() === WebInspector.DOMNode.PseudoElementType.After, "New DOMNode is an after pseudo element");
+
+                previousInsertedDOMNode = insertedOrRemovedDOMNode;
+                next();
+            }
+        },
+        {
+            name: "Remove :after",
+            action: function() {
+                InspectorTest.evaluateInPage("document.getElementById('x').classList.remove('a')");
+            }
+        },
+        {
+            name: "Verify :after was removed",
+            action: function() {
+                InspectorTest.expectThat(!testDOMNode.hasPseudoElements(), "#x DOMNode has no pseudo elements");
+                InspectorTest.assert(testDOMNode.pseudoElements().size === 0);
+                InspectorTest.assert(!testDOMNode.beforePseudoElement());
+                InspectorTest.assert(!testDOMNode.afterPseudoElement());
+
+                InspectorTest.expectThat(insertedOrRemovedDOMNode === previousInsertedDOMNode, "Removed DOMNode was the after pseudo element");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.parentNode === null, "Removed DOMNode has no parent");
+                InspectorTest.expectThat(insertedOrRemovedDOMNode.pseudoType() === WebInspector.DOMNode.PseudoElementType.After, "Removed DOMNode was an after pseudo element");
+
+                previousInsertedDOMNode = null;
+                next();
+            }
+        },
+    ];
+
+    function next()
+    {
+        var step = steps.shift();
+        if (step) {
+            InspectorTest.log("");
+            InspectorTest.log(step.name);
+            step.action();
+            return;
+        }
+        InspectorTest.completeTest();
+    }
+
+    WebInspector.domTreeManager.requestDocument(function(documentNode) {
+        WebInspector.domTreeManager.querySelector(documentNode.id, "#x", function(nodeId) {
+            testDOMNode = WebInspector.domTreeManager.nodeForId(nodeId);
+            InspectorTest.log("");
+            InspectorTest.expectThat(testDOMNode, "Got DOMNode for #x");
+            InspectorTest.assert(!testDOMNode.pseudoType());
+            InspectorTest.expectThat(!testDOMNode.hasPseudoElements(), "DOMNode has no pseudo elements");
+            InspectorTest.assert(testDOMNode.pseudoElements().size === 0);
+            InspectorTest.assert(!testDOMNode.beforePseudoElement());
+            InspectorTest.assert(!testDOMNode.afterPseudoElement());
+            next();
+        });
+    });
+
+    WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.NodeInserted, function(event) {
+        // Test logging will add nodes to the test page. Ignore any elements added that are not children of our test node.
+        if (event.data.parent !== testDOMNode)
+            return;
+
+        InspectorTest.log("EVENT: NodeInserted");
+        insertedOrRemovedDOMNode = event.data.node;
+        next();
+    });
+
+    WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.NodeRemoved, function(event) {
+        InspectorTest.log("EVENT: NodeRemoved");
+        insertedOrRemovedDOMNode = event.data.node;
+        next();
+    });
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <p>Test for dynamic DOMNode pseudo element support, pseudo elements getting added and removed dynamically.</p>
+
+    <style>
+    .b:before { content: "before"; }
+    .a:after { content: "after"; }
+    </style>
+
+    <div id="x">TEST ELEMENT</div>
+</body>
+</html>

Added: branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-static-expected.txt (0 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-static-expected.txt	                        (rev 0)
+++ branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-static-expected.txt	2015-12-04 20:51:49 UTC (rev 193441)
@@ -0,0 +1,32 @@
+Test for basic DOMNode pseudo element support.
+
+TEST ELEMENT: No Pseudo Elements
+TEST ELEMENT: Has Before Pseudo Element
+TEST ELEMENT: Has After Pseudo Element
+TEST ELEMENT: Has Before and After Pseudo Elements
+
+PASS: Got DOMNode for #none
+PASS: DOMNode has no pseudo elements
+
+PASS: Got DOMNode for #b
+PASS: DOMNode has pseudo elements
+PASS: DOMNode has 1 pseudo elements
+PASS: DOMNode has a before pseudo element
+PASS: DOMNode does not have an after pseudo element
+PASS: DOMNode for before pseudo element has the right type
+
+PASS: Got DOMNode for #a
+PASS: DOMNode has pseudo elements
+PASS: DOMNode has 1 pseudo elements
+PASS: DOMNode does not have a before pseudo element
+PASS: DOMNode has an after pseudo element
+PASS: DOMNode for after pseudo element has the right type
+
+PASS: Got DOMNode for #x
+PASS: DOMNode has pseudo elements
+PASS: DOMNode has 2 pseudo elements
+PASS: DOMNode has a before pseudo element
+PASS: DOMNode has an after pseudo element
+PASS: DOMNode for before pseudo element has the right type
+PASS: DOMNode for after pseudo element has the right type
+

Added: branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-static.html (0 => 193441)


--- branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-static.html	                        (rev 0)
+++ branches/safari-601.1.46-branch/LayoutTests/inspector/dom/pseudo-element-static.html	2015-12-04 20:51:49 UTC (rev 193441)
@@ -0,0 +1,73 @@
+<html>
+<head>
+<script src=""
+<script>
+function test() {
+    WebInspector.domTreeManager.requestDocument(function(documentNode) {
+        WebInspector.domTreeManager.querySelector(documentNode.id, "#none", function(nodeId) {
+            var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
+            InspectorTest.log("");
+            InspectorTest.expectThat(domNode, "Got DOMNode for #none");
+            InspectorTest.assert(!domNode.pseudoType());
+            InspectorTest.expectThat(!domNode.hasPseudoElements(), "DOMNode has no pseudo elements");
+            InspectorTest.assert(domNode.pseudoElements().size === 0);
+            InspectorTest.assert(!domNode.beforePseudoElement());
+            InspectorTest.assert(!domNode.afterPseudoElement());
+        });
+
+        WebInspector.domTreeManager.querySelector(documentNode.id, "#b", function(nodeId) {
+            var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
+            InspectorTest.log("");
+            InspectorTest.expectThat(domNode, "Got DOMNode for #b");
+            InspectorTest.assert(!domNode.pseudoType());
+            InspectorTest.expectThat(domNode.hasPseudoElements(), "DOMNode has pseudo elements");
+            InspectorTest.expectThat(domNode.pseudoElements().size === 1, "DOMNode has 1 pseudo elements");
+            InspectorTest.expectThat(domNode.beforePseudoElement(), "DOMNode has a before pseudo element");
+            InspectorTest.expectThat(!domNode.afterPseudoElement(), "DOMNode does not have an after pseudo element");
+            InspectorTest.expectThat(domNode.beforePseudoElement().pseudoType() === WebInspector.DOMNode.PseudoElementType.Before, "DOMNode for before pseudo element has the right type");
+        });
+
+        WebInspector.domTreeManager.querySelector(documentNode.id, "#a", function(nodeId) {
+            var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
+            InspectorTest.log("");
+            InspectorTest.expectThat(domNode, "Got DOMNode for #a");
+            InspectorTest.assert(!domNode.pseudoType());
+            InspectorTest.expectThat(domNode.hasPseudoElements(), "DOMNode has pseudo elements");
+            InspectorTest.expectThat(domNode.pseudoElements().size === 1, "DOMNode has 1 pseudo elements");
+            InspectorTest.expectThat(!domNode.beforePseudoElement(), "DOMNode does not have a before pseudo element");
+            InspectorTest.expectThat(domNode.afterPseudoElement(), "DOMNode has an after pseudo element");
+            InspectorTest.expectThat(domNode.afterPseudoElement().pseudoType() === WebInspector.DOMNode.PseudoElementType.After, "DOMNode for after pseudo element has the right type");
+        });
+
+        WebInspector.domTreeManager.querySelector(documentNode.id, "#x", function(nodeId) {
+            var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
+            InspectorTest.log("");
+            InspectorTest.expectThat(domNode, "Got DOMNode for #x");
+            InspectorTest.assert(!domNode.pseudoType());
+            InspectorTest.expectThat(domNode.hasPseudoElements(), "DOMNode has pseudo elements");
+            InspectorTest.expectThat(domNode.pseudoElements().size === 2, "DOMNode has 2 pseudo elements");
+            InspectorTest.expectThat(domNode.beforePseudoElement(), "DOMNode has a before pseudo element");
+            InspectorTest.expectThat(domNode.afterPseudoElement(), "DOMNode has an after pseudo element");
+            InspectorTest.expectThat(domNode.beforePseudoElement().pseudoType() === WebInspector.DOMNode.PseudoElementType.Before, "DOMNode for before pseudo element has the right type");
+            InspectorTest.expectThat(domNode.afterPseudoElement().pseudoType() === WebInspector.DOMNode.PseudoElementType.After, "DOMNode for after pseudo element has the right type");
+            InspectorTest.completeTest();
+        });
+    });
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <p>Test for basic DOMNode pseudo element support.</p>
+
+    <style>
+    #x:before, #b:before { content: "before"; }
+    #x:after, #a:after { content: "after"; }
+    </style>
+
+    <div id="none">TEST ELEMENT: No Pseudo Elements</div>
+    <div id="b">TEST ELEMENT: Has Before Pseudo Element</div>
+    <div id="a">TEST ELEMENT: Has After Pseudo Element</div>
+    <div id="x">TEST ELEMENT: Has Before and After Pseudo Elements</div>
+
+</body>
+</html>

Modified: branches/safari-601.1.46-branch/Source/_javascript_Core/ChangeLog (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/_javascript_Core/ChangeLog	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/_javascript_Core/ChangeLog	2015-12-04 20:51:49 UTC (rev 193441)
@@ -1,5 +1,21 @@
 2015-12-04  Timothy Hatcher  <[email protected]>
 
+        Merge r187496. rdar://problem/23581597
+
+    2015-07-28  Joseph Pecoraro  <[email protected]>
+
+            Web Inspector: Show Pseudo Elements in DOM Tree
+            https://bugs.webkit.org/show_bug.cgi?id=139612
+
+            Reviewed by Timothy Hatcher.
+
+            * inspector/protocol/DOM.json:
+            Add new properties to DOMNode if it is a pseudo element or if it has
+            pseudo element children. Add new events for if a pseudo element is
+            added or removed dynamically to an existing DOMNode.
+
+2015-12-04  Timothy Hatcher  <[email protected]>
+
         Merge r187249. rdar://problem/23581597
 
     2015-07-23  Devin Rousso  <[email protected]>

Modified: branches/safari-601.1.46-branch/Source/_javascript_Core/inspector/protocol/DOM.json (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/_javascript_Core/inspector/protocol/DOM.json	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/_javascript_Core/inspector/protocol/DOM.json	2015-12-04 20:51:49 UTC (rev 193441)
@@ -14,6 +14,12 @@
             "description": "Unique DOM node identifier used to reference a node that may not have been pushed to the front-end."
         },
         {
+            "id": "PseudoType",
+            "type": "string",
+            "enum": ["before", "after"],
+            "description": "Pseudo element type."
+        },
+        {
             "id": "LiveRegionRelevant",
             "type": "string",
             "enum": ["additions", "removals", "text"],
@@ -39,10 +45,12 @@
                 { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." },
                 { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." },
                 { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." },
+                { "name": "pseudoType", "$ref": "PseudoType", "optional": true, "description": "Pseudo element type for this node." },
                 { "name": "frameId", "$ref": "Network.FrameId", "optional": true, "description": "Frame ID for frame owner elements." },
                 { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." },
                 { "name": "shadowRoots", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Shadow root list for given element host." },
                 { "name": "templateContent", "$ref": "Node", "optional": true, "description": "Content document fragment for template elements" },
+                { "name": "pseudoElements", "type": "array", "items": { "$ref": "Node" }, "optional": true, "description": "Pseudo elements associated with this node." },
                 { "name": "role", "type": "string", "optional": true, "description": "Computed value for first recognized role token, default role per element, or overridden role." }
             ],
             "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
@@ -526,6 +534,22 @@
                 { "name": "rootId", "$ref": "NodeId", "description": "Shadow root id." }
             ],
             "description": "Called when shadow root is popped from the element."
+        },
+        {
+            "name": "pseudoElementAdded",
+            "parameters": [
+                { "name": "parentId", "$ref": "NodeId", "description": "Pseudo element's parent element id." },
+                { "name": "pseudoElement", "$ref": "Node", "description": "The added pseudo element." }
+            ],
+            "description": "Called when a pseudo element is added to an element."
+        },
+        {
+            "name": "pseudoElementRemoved",
+            "parameters": [
+                { "name": "parentId", "$ref": "NodeId", "description": "Pseudo element's parent element id." },
+                { "name": "pseudoElementId", "$ref": "NodeId", "description": "The removed pseudo element id." }
+            ],
+            "description": "Called when a pseudo element is removed from an element."
         }
     ]
 }

Modified: branches/safari-601.1.46-branch/Source/WebCore/ChangeLog (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/ChangeLog	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/ChangeLog	2015-12-04 20:51:49 UTC (rev 193441)
@@ -1,5 +1,84 @@
 2015-12-04  Timothy Hatcher  <[email protected]>
 
+        Merge r187496. rdar://problem/23581597
+
+    2015-07-28  Joseph Pecoraro  <[email protected]>
+
+            Web Inspector: Show Pseudo Elements in DOM Tree
+            https://bugs.webkit.org/show_bug.cgi?id=139612
+
+            Reviewed by Timothy Hatcher.
+
+            Tests: inspector/css/pseudo-element-matches-for-pseudo-element-node.html
+                   inspector/dom/pseudo-element-dynamic.html
+                   inspector/dom/pseudo-element-static.html
+
+            Much of this patch was modelled after the Blink implementation of
+            pseudo element inspection.
+
+            * dom/PseudoElement.h:
+            * dom/PseudoElement.cpp:
+            (WebCore::PseudoElement::~PseudoElement):
+            (WebCore::PseudoElement::clearHostElement):
+            Since InspectorDOMAgent may hold a reference to this PseudoElement we
+            can't report it as destroyed in the destructor, as that wouldn't be
+            reached if the inspector holds a reference. Move this to when the
+            psuedo element is disconnected, which is immediately before destruction.
+
+            * inspector/InspectorCSSAgent.h:
+            * inspector/InspectorCSSAgent.cpp:
+            (WebCore::InspectorCSSAgent::getMatchedStylesForNode):
+            When computing styles for a pseudo element, compute styles from the
+            host element for just the pseudo element's pseudo type. Likewise
+            only include matched results, not inherited or others.
+
+            (WebCore::InspectorCSSAgent::buildArrayForMatchedRuleList):
+            Add the pseudo type to the checker context to try and detect exactly
+            which selector in a list of selectors matched the pseudo element.
+
+            * inspector/InspectorDOMAgent.h:
+            * inspector/InspectorDOMAgent.cpp:
+            (WebCore::InspectorDOMAgent::unbind):
+            When unbinding an element, also unbind any pseudo element children
+            it may have had and bound.
+
+            (WebCore::InspectorDOMAgent::assertEditableNode):
+            (WebCore::InspectorDOMAgent::assertEditableElement):
+            (WebCore::InspectorDOMAgent::removeNode):
+            Improve grammar in error message. Don't allow editing pseudo elements.
+
+            (WebCore::pseudoElementType):
+            (WebCore::InspectorDOMAgent::buildObjectForNode):
+            (WebCore::InspectorDOMAgent::buildArrayForPseudoElements):
+            If a node is a pseudo element include its pseudoType.
+            If a node has pseudo element children include them.
+
+            (WebCore::InspectorDOMAgent::pseudoElementCreated):
+            (WebCore::InspectorDOMAgent::pseudoElementDestroyed):
+            When pseudo elements are dynamically created or destroyed
+            push pseudo element nodes to the frontend if needed.
+
+            * inspector/InspectorInstrumentation.cpp:
+            (WebCore::InspectorInstrumentation::pseudoElementCreatedImpl):
+            (WebCore::InspectorInstrumentation::pseudoElementDestroyedImpl):
+            * inspector/InspectorInstrumentation.h:
+            (WebCore::InspectorInstrumentation::pseudoElementCreated):
+            (WebCore::InspectorInstrumentation::pseudoElementDestroyed):
+            (WebCore::InspectorInstrumentation::layerTreeDidChange):
+            (WebCore::InspectorInstrumentation::renderLayerDestroyed):
+            Plumbing for pseudo element created/destroyed events.
+
+            * style/StyleResolveTree.cpp:
+            (WebCore::Style::attachBeforeOrAfterPseudoElementIfNeeded):
+            This is the only place a pseudo element is created, inform the inspector.
+
+            * inspector/InspectorOverlay.cpp:
+            (WebCore::buildObjectForElementData):
+            Update the element data for the node highlight label to include the
+            host element's selector and the pseudo element selector.
+
+2015-12-04  Timothy Hatcher  <[email protected]>
+
         Merge r188227. rdar://problem/23581597
 
     2015-08-10  Devin Rousso  <[email protected]>

Modified: branches/safari-601.1.46-branch/Source/WebCore/dom/PseudoElement.cpp (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/dom/PseudoElement.cpp	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/dom/PseudoElement.cpp	2015-12-04 20:51:49 UTC (rev 193441)
@@ -67,7 +67,13 @@
 PseudoElement::~PseudoElement()
 {
     ASSERT(!m_hostElement);
+}
+
+void PseudoElement::clearHostElement()
+{
     InspectorInstrumentation::pseudoElementDestroyed(document().page(), *this);
+
+    m_hostElement = nullptr;
 }
 
 RefPtr<RenderStyle> PseudoElement::customStyleForRenderer(RenderStyle& parentStyle)

Modified: branches/safari-601.1.46-branch/Source/WebCore/dom/PseudoElement.h (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/dom/PseudoElement.h	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/dom/PseudoElement.h	2015-12-04 20:51:49 UTC (rev 193441)
@@ -43,7 +43,7 @@
     virtual ~PseudoElement();
 
     Element* hostElement() const { return m_hostElement; }
-    void clearHostElement() { m_hostElement = nullptr; }
+    void clearHostElement();
 
     virtual RefPtr<RenderStyle> customStyleForRenderer(RenderStyle& parentStyle) override;
     virtual void didAttachRenderers() override;

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorCSSAgent.cpp (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorCSSAgent.cpp	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorCSSAgent.cpp	2015-12-04 20:51:49 UTC (rev 193441)
@@ -45,6 +45,7 @@
 #include "NamedFlowCollection.h"
 #include "Node.h"
 #include "NodeList.h"
+#include "PseudoElement.h"
 #include "RenderNamedFlowFragment.h"
 #include "SVGStyleElement.h"
 #include "SelectorChecker.h"
@@ -510,49 +511,60 @@
     if (!element)
         return;
 
+    Element* originalElement = element;
+    PseudoId elementPseudoId = element->pseudoId();
+    if (elementPseudoId) {
+        element = downcast<PseudoElement>(*element).hostElement();
+        if (!element) {
+            errorString = ASCIILiteral("Pseudo element has no parent");
+            return;
+        }
+    }
+
     // Matched rules.
     StyleResolver& styleResolver = element->document().ensureStyleResolver();
-    auto matchedRules = styleResolver.styleRulesForElement(element, StyleResolver::AllCSSRules);
-    matchedCSSRules = buildArrayForMatchedRuleList(matchedRules, styleResolver, element);
+    auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules);
+    matchedCSSRules = buildArrayForMatchedRuleList(matchedRules, styleResolver, element, elementPseudoId);
 
-    // Pseudo elements.
-    if (!includePseudo || *includePseudo) {
-        auto pseudoElements = Inspector::Protocol::Array<Inspector::Protocol::CSS::PseudoIdMatches>::create();
-        for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
-            auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
-            if (!matchedRules.isEmpty()) {
-                auto matches = Inspector::Protocol::CSS::PseudoIdMatches::create()
-                    .setPseudoId(static_cast<int>(pseudoId))
-                    .setMatches(buildArrayForMatchedRuleList(matchedRules, styleResolver, element))
-                    .release();
-                pseudoElements->addItem(WTF::move(matches));
+    if (!originalElement->isPseudoElement()) {
+        // Pseudo elements.
+        if (!includePseudo || *includePseudo) {
+            auto pseudoElements = Inspector::Protocol::Array<Inspector::Protocol::CSS::PseudoIdMatches>::create();
+            for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
+                auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
+                if (!matchedRules.isEmpty()) {
+                    auto matches = Inspector::Protocol::CSS::PseudoIdMatches::create()
+                        .setPseudoId(static_cast<int>(pseudoId))
+                        .setMatches(buildArrayForMatchedRuleList(matchedRules, styleResolver, element, pseudoId))
+                        .release();
+                    pseudoElements->addItem(WTF::move(matches));
+                }
             }
+
+            pseudoIdMatches = WTF::move(pseudoElements);
         }
 
-        pseudoIdMatches = WTF::move(pseudoElements);
-    }
+        // Inherited styles.
+        if (!includeInherited || *includeInherited) {
+            auto entries = Inspector::Protocol::Array<Inspector::Protocol::CSS::InheritedStyleEntry>::create();
+            Element* parentElement = element->parentElement();
+            while (parentElement) {
+                StyleResolver& parentStyleResolver = parentElement->document().ensureStyleResolver();
+                auto parentMatchedRules = parentStyleResolver.styleRulesForElement(parentElement, StyleResolver::AllCSSRules);
+                auto entry = Inspector::Protocol::CSS::InheritedStyleEntry::create()
+                    .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules, styleResolver, parentElement, NOPSEUDO))
+                    .release();
+                if (parentElement->style() && parentElement->style()->length()) {
+                    if (InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement))
+                        entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
+                }
 
-    // Inherited styles.
-    if (!includeInherited || *includeInherited) {
-        auto entries = Inspector::Protocol::Array<Inspector::Protocol::CSS::InheritedStyleEntry>::create();
-        Element* parentElement = element->parentElement();
-        while (parentElement) {
-            StyleResolver& parentStyleResolver = parentElement->document().ensureStyleResolver();
-            auto parentMatchedRules = parentStyleResolver.styleRulesForElement(parentElement, StyleResolver::AllCSSRules);
-            auto entry = Inspector::Protocol::CSS::InheritedStyleEntry::create()
-                .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules, styleResolver, parentElement))
-                .release();
-            if (parentElement->style() && parentElement->style()->length()) {
-                InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
-                if (styleSheet)
-                    entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
+                entries->addItem(WTF::move(entry));
+                parentElement = parentElement->parentElement();
             }
 
-            entries->addItem(WTF::move(entry));
-            parentElement = parentElement->parentElement();
+            inheritedEntries = WTF::move(entries);
         }
-
-        inheritedEntries = WTF::move(entries);
     }
 }
 
@@ -924,11 +936,12 @@
     return inspectorStyleSheet ? inspectorStyleSheet->buildObjectForRule(rule, nullptr) : nullptr;
 }
 
-RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::RuleMatch>> InspectorCSSAgent::buildArrayForMatchedRuleList(const Vector<RefPtr<StyleRule>>& matchedRules, StyleResolver& styleResolver, Element* element)
+RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::RuleMatch>> InspectorCSSAgent::buildArrayForMatchedRuleList(const Vector<RefPtr<StyleRule>>& matchedRules, StyleResolver& styleResolver, Element* element, PseudoId psuedoId)
 {
     auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::RuleMatch>::create();
 
     SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules);
+    context.pseudoId = psuedoId ? psuedoId : element->pseudoId();
     SelectorChecker selectorChecker(element->document());
 
     for (auto& matchedRule : matchedRules) {

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorCSSAgent.h (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorCSSAgent.h	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorCSSAgent.h	2015-12-04 20:51:49 UTC (rev 193441)
@@ -149,7 +149,7 @@
 
     RefPtr<Inspector::Protocol::CSS::CSSRule> buildObjectForRule(StyleRule*, StyleResolver&, Element*);
     RefPtr<Inspector::Protocol::CSS::CSSRule> buildObjectForRule(CSSStyleRule*);
-    RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::RuleMatch>> buildArrayForMatchedRuleList(const Vector<RefPtr<StyleRule>>&, StyleResolver&, Element*);
+    RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::RuleMatch>> buildArrayForMatchedRuleList(const Vector<RefPtr<StyleRule>>&, StyleResolver&, Element*, PseudoId);
     RefPtr<Inspector::Protocol::CSS::CSSStyle> buildObjectForAttributesStyle(Element*);
     RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::Region>> buildArrayForRegions(ErrorString&, RefPtr<NodeList>&&, int documentNodeId);
     RefPtr<Inspector::Protocol::CSS::NamedFlow> buildObjectForNamedFlow(ErrorString&, WebKitNamedFlow*, int documentNodeId);

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorDOMAgent.cpp (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorDOMAgent.cpp	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorDOMAgent.cpp	2015-12-04 20:51:49 UTC (rev 193441)
@@ -75,6 +75,7 @@
 #include "NodeList.h"
 #include "Page.h"
 #include "Pasteboard.h"
+#include "PseudoElement.h"
 #include "RenderStyle.h"
 #include "RenderStyleConstants.h"
 #include "ScriptState.h"
@@ -332,8 +333,13 @@
     }
 
     if (is<Element>(*node)) {
-        if (ShadowRoot* root = downcast<Element>(*node).shadowRoot())
+        Element& element = downcast<Element>(*node);
+        if (ShadowRoot* root = element.shadowRoot())
             unbind(root, nodesMap);
+        if (PseudoElement* beforeElement = element.beforePseudoElement())
+            unbind(beforeElement, nodesMap);
+        if (PseudoElement* afterElement = element.afterPseudoElement())
+            unbind(afterElement, nodesMap);
     }
 
     nodesMap->remove(node);
@@ -392,9 +398,13 @@
     if (!node)
         return nullptr;
     if (node->isInShadowTree()) {
-        errorString = ASCIILiteral("Can not edit nodes from shadow trees");
+        errorString = ASCIILiteral("Cannot edit nodes from shadow trees");
         return nullptr;
     }
+    if (node->isPseudoElement()) {
+        errorString = ASCIILiteral("Cannot edit pseudo elements");
+        return nullptr;
+    }
     return node;
 }
 
@@ -404,9 +414,13 @@
     if (!element)
         return nullptr;
     if (element->isInShadowTree()) {
-        errorString = ASCIILiteral("Can not edit elements from shadow trees");
+        errorString = ASCIILiteral("Cannot edit elements from shadow trees");
         return nullptr;
     }
+    if (element->isPseudoElement()) {
+        errorString = ASCIILiteral("Cannot edit pseudo elements");
+        return nullptr;
+    }
     return element;
 }
 
@@ -698,7 +712,7 @@
 
     ContainerNode* parentNode = node->parentNode();
     if (!parentNode) {
-        errorString = ASCIILiteral("Can not remove detached node");
+        errorString = ASCIILiteral("Cannot remove detached node");
         return;
     }
 
@@ -1254,6 +1268,20 @@
     return document->completeURL("").string();
 }
 
+static bool pseudoElementType(PseudoId pseudoId, Inspector::Protocol::DOM::PseudoType* type)
+{
+    switch (pseudoId) {
+    case BEFORE:
+        *type = Inspector::Protocol::DOM::PseudoType::Before;
+        return true;
+    case AFTER:
+        *type = Inspector::Protocol::DOM::PseudoType::After;
+        return true;
+    default:
+        return false;
+    }
+}
+
 Ref<Inspector::Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
 {
     int id = bind(node, nodesMap);
@@ -1327,6 +1355,15 @@
             value->setTemplateContent(buildObjectForNode(downcast<HTMLTemplateElement>(element).content(), 0, nodesMap));
 #endif
 
+        if (element.pseudoId()) {
+            Inspector::Protocol::DOM::PseudoType pseudoType;
+            if (pseudoElementType(element.pseudoId(), &pseudoType))
+                value->setPseudoType(pseudoType);
+        } else {
+            if (auto pseudoElements = buildArrayForPseudoElements(element, nodesMap))
+                value->setPseudoElements(WTF::move(pseudoElements));
+        }
+
     } else if (is<Document>(*node)) {
         Document& document = downcast<Document>(*node);
         value->setFrameId(m_pageAgent->frameId(document.frame()));
@@ -1394,6 +1431,21 @@
     return WTF::move(children);
 }
 
+RefPtr<Inspector::Protocol::Array<Inspector::Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForPseudoElements(const Element& element, NodeToIdMap* nodesMap)
+{
+    PseudoElement* beforeElement = element.beforePseudoElement();
+    PseudoElement* afterElement = element.afterPseudoElement();
+    if (!beforeElement && !afterElement)
+        return nullptr;
+
+    auto pseudoElements = Inspector::Protocol::Array<Inspector::Protocol::DOM::Node>::create();
+    if (beforeElement)
+        pseudoElements->addItem(buildObjectForNode(beforeElement, 0, nodesMap));
+    if (afterElement)
+        pseudoElements->addItem(buildObjectForNode(afterElement, 0, nodesMap));
+    return WTF::move(pseudoElements);
+}
+
 Ref<Inspector::Protocol::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
 {
     RefPtr<EventListener> eventListener = registeredEventListener.listener;
@@ -1957,6 +2009,36 @@
     setDocument(document);
 }
 
+void InspectorDOMAgent::pseudoElementCreated(PseudoElement& pseudoElement)
+{
+    Element* parent = pseudoElement.hostElement();
+    if (!parent)
+        return;
+
+    int parentId = m_documentNodeToIdMap.get(parent);
+    if (!parentId)
+        return;
+
+    pushChildNodesToFrontend(parentId, 1);
+    m_frontendDispatcher->pseudoElementAdded(parentId, buildObjectForNode(&pseudoElement, 0, &m_documentNodeToIdMap));
+}
+
+void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement& pseudoElement)
+{
+    int pseudoElementId = m_documentNodeToIdMap.get(&pseudoElement);
+    if (!pseudoElementId)
+        return;
+
+    // If a PseudoElement is bound, its parent element must have been bound.
+    Element* parent = pseudoElement.hostElement();
+    ASSERT(parent);
+    int parentId = m_documentNodeToIdMap.get(parent);
+    ASSERT(parentId);
+
+    unbind(&pseudoElement, &m_documentNodeToIdMap);
+    m_frontendDispatcher->pseudoElementRemoved(parentId, pseudoElementId);
+}
+
 Node* InspectorDOMAgent::nodeForPath(const String& path)
 {
     // The path is of form "1,HTML,2,BODY,1,DIV"

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorDOMAgent.h (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorDOMAgent.h	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorDOMAgent.h	2015-12-04 20:51:49 UTC (rev 193441)
@@ -169,6 +169,8 @@
     bool handleTouchEvent(Node&);
     void didCommitLoad(Document*);
     void frameDocumentUpdated(Frame*);
+    void pseudoElementCreated(PseudoElement&);
+    void pseudoElementDestroyed(PseudoElement&);
 
     // Callbacks that don't directly correspond to an instrumentation entry point.
     void setDocument(Document*);
@@ -233,6 +235,7 @@
     Ref<Inspector::Protocol::DOM::Node> buildObjectForNode(Node*, int depth, NodeToIdMap*);
     Ref<Inspector::Protocol::Array<String>> buildArrayForElementAttributes(Element*);
     Ref<Inspector::Protocol::Array<Inspector::Protocol::DOM::Node>> buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap);
+    RefPtr<Inspector::Protocol::Array<Inspector::Protocol::DOM::Node>> buildArrayForPseudoElements(const Element&, NodeToIdMap* nodesMap);
     Ref<Inspector::Protocol::DOM::EventListener> buildObjectForEventListener(const RegisteredEventListener&, const AtomicString& eventType, Node*, const String* objectGroupId);
     RefPtr<Inspector::Protocol::DOM::AccessibilityProperties> buildObjectForAccessibilityProperties(Node*);
     void processAccessibilityChildren(RefPtr<AccessibilityObject>&&, RefPtr<Inspector::Protocol::Array<int>>&&);

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorInstrumentation.cpp (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorInstrumentation.cpp	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorInstrumentation.cpp	2015-12-04 20:51:49 UTC (rev 193441)
@@ -208,6 +208,20 @@
         domAgent->willPopShadowRoot(host, root);
 }
 
+void InspectorInstrumentation::pseudoElementCreatedImpl(InstrumentingAgents& instrumentingAgents, PseudoElement& pseudoElement)
+{
+    if (InspectorDOMAgent* domAgent = instrumentingAgents.inspectorDOMAgent())
+        domAgent->pseudoElementCreated(pseudoElement);
+}
+
+void InspectorInstrumentation::pseudoElementDestroyedImpl(InstrumentingAgents& instrumentingAgents, PseudoElement& pseudoElement)
+{
+    if (InspectorDOMAgent* domAgent = instrumentingAgents.inspectorDOMAgent())
+        domAgent->pseudoElementDestroyed(pseudoElement);
+    if (InspectorLayerTreeAgent* layerTreeAgent = instrumentingAgents.inspectorLayerTreeAgent())
+        layerTreeAgent->pseudoElementDestroyed(pseudoElement);
+}
+
 void InspectorInstrumentation::didCreateNamedFlowImpl(InstrumentingAgents& instrumentingAgents, Document* document, WebKitNamedFlow& namedFlow)
 {
     if (!document)
@@ -1287,10 +1301,4 @@
         layerTreeAgent->renderLayerDestroyed(renderLayer);
 }
 
-void InspectorInstrumentation::pseudoElementDestroyedImpl(InstrumentingAgents& instrumentingAgents, PseudoElement& pseudoElement)
-{
-    if (InspectorLayerTreeAgent* layerTreeAgent = instrumentingAgents.inspectorLayerTreeAgent())
-        layerTreeAgent->pseudoElementDestroyed(pseudoElement);
-}
-
 } // namespace WebCore

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorInstrumentation.h (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorInstrumentation.h	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorInstrumentation.h	2015-12-04 20:51:49 UTC (rev 193441)
@@ -75,9 +75,9 @@
 class DOMWrapperWorld;
 class Database;
 class Document;
-class Element;
 class DocumentLoader;
 class DocumentStyleSheetCollection;
+class Element;
 class GraphicsContext;
 class HTTPHeaderMap;
 class InspectorCSSAgent;
@@ -85,7 +85,6 @@
 class InspectorInstrumentation;
 class InspectorTimelineAgent;
 class InstrumentingAgents;
-class URL;
 class Node;
 class PseudoElement;
 class RenderLayer;
@@ -100,6 +99,7 @@
 class StyleResolver;
 class StyleRule;
 class ThreadableLoaderClient;
+class URL;
 class WorkerGlobalScope;
 class WorkerGlobalScopeProxy;
 class XMLHttpRequest;
@@ -126,6 +126,8 @@
     static void mediaQueryResultChanged(Document&);
     static void didPushShadowRoot(Element& host, ShadowRoot&);
     static void willPopShadowRoot(Element& host, ShadowRoot&);
+    static void pseudoElementCreated(Page*, PseudoElement&);
+    static void pseudoElementDestroyed(Page*, PseudoElement&);
     static void didCreateNamedFlow(Document*, WebKitNamedFlow&);
     static void willRemoveNamedFlow(Document*, WebKitNamedFlow&);
     static void didChangeRegionOverset(Document&, WebKitNamedFlow&);
@@ -275,7 +277,6 @@
 
     static void layerTreeDidChange(Page*);
     static void renderLayerDestroyed(Page*, const RenderLayer&);
-    static void pseudoElementDestroyed(Page*, PseudoElement&);
 
     static void frontendCreated() { s_frontendCounter += 1; }
     static void frontendDeleted() { s_frontendCounter -= 1; }
@@ -306,6 +307,8 @@
     static void mediaQueryResultChangedImpl(InstrumentingAgents&);
     static void didPushShadowRootImpl(InstrumentingAgents&, Element& host, ShadowRoot&);
     static void willPopShadowRootImpl(InstrumentingAgents&, Element& host, ShadowRoot&);
+    static void pseudoElementCreatedImpl(InstrumentingAgents&, PseudoElement&);
+    static void pseudoElementDestroyedImpl(InstrumentingAgents&, PseudoElement&);
     static void didCreateNamedFlowImpl(InstrumentingAgents&, Document*, WebKitNamedFlow&);
     static void willRemoveNamedFlowImpl(InstrumentingAgents&, Document*, WebKitNamedFlow&);
     static void didChangeRegionOversetImpl(InstrumentingAgents&, Document&, WebKitNamedFlow&);
@@ -449,7 +452,6 @@
 
     static void layerTreeDidChangeImpl(InstrumentingAgents&);
     static void renderLayerDestroyedImpl(InstrumentingAgents&, const RenderLayer&);
-    static void pseudoElementDestroyedImpl(InstrumentingAgents&, PseudoElement&);
 
     static InstrumentingAgents* instrumentingAgentsForPage(Page&);
     static InstrumentingAgents* instrumentingAgentsForFrame(Frame&);
@@ -567,6 +569,20 @@
         willPopShadowRootImpl(*instrumentingAgents, host, root);
 }
 
+inline void InspectorInstrumentation::pseudoElementCreated(Page* page, PseudoElement& pseudoElement)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+        pseudoElementCreatedImpl(*instrumentingAgents, pseudoElement);
+}
+
+inline void InspectorInstrumentation::pseudoElementDestroyed(Page* page, PseudoElement& pseudoElement)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+        pseudoElementDestroyedImpl(*instrumentingAgents, pseudoElement);
+}
+
 inline void InspectorInstrumentation::didCreateNamedFlow(Document* document, WebKitNamedFlow& namedFlow)
 {
     FAST_RETURN_IF_NO_FRONTENDS(void());
@@ -1308,22 +1324,18 @@
 
 inline void InspectorInstrumentation::layerTreeDidChange(Page* page)
 {
+    FAST_RETURN_IF_NO_FRONTENDS(void());
     if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
         layerTreeDidChangeImpl(*instrumentingAgents);
 }
 
 inline void InspectorInstrumentation::renderLayerDestroyed(Page* page, const RenderLayer& renderLayer)
 {
+    FAST_RETURN_IF_NO_FRONTENDS(void());
     if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
         renderLayerDestroyedImpl(*instrumentingAgents, renderLayer);
 }
 
-inline void InspectorInstrumentation::pseudoElementDestroyed(Page* page, PseudoElement& pseudoElement)
-{
-    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
-        pseudoElementDestroyedImpl(*instrumentingAgents, pseudoElement);
-}
-
 inline InstrumentingAgents* InspectorInstrumentation::instrumentingAgentsForContext(ScriptExecutionContext* context)
 {
     if (!context)

Modified: branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorOverlay.cpp (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorOverlay.cpp	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/inspector/InspectorOverlay.cpp	2015-12-04 20:51:49 UTC (rev 193441)
@@ -41,6 +41,7 @@
 #include "Page.h"
 #include "PageConfiguration.h"
 #include "PolygonShape.h"
+#include "PseudoElement.h"
 #include "RectangleShape.h"
 #include "RenderBoxModelObject.h"
 #include "RenderElement.h"
@@ -686,17 +687,24 @@
     if (!is<Element>(node) || !node->document().frame())
         return nullptr;
 
-    Element& element = downcast<Element>(*node);
+    Element* effectiveElement = downcast<Element>(node);
+    if (node->isPseudoElement()) {
+        Element* hostElement = downcast<PseudoElement>(*node).hostElement();
+        if (!hostElement)
+            return nullptr;
+        effectiveElement = hostElement;
+    }
+
+    Element& element = *effectiveElement;
     bool isXHTML = element.document().isXHTMLDocument();
-
     auto elementData = Inspector::Protocol::OverlayTypes::ElementData::create()
         .setTagName(isXHTML ? element.nodeName() : element.nodeName().lower())
         .setIdValue(element.getIdAttribute())
         .release();
 
-    HashSet<AtomicString> usedClassNames;
+    StringBuilder classNames;
     if (element.hasClass() && is<StyledElement>(element)) {
-        StringBuilder classNames;
+        HashSet<AtomicString> usedClassNames;
         const SpaceSplitString& classNamesString = downcast<StyledElement>(element).classNames();
         size_t classNameCount = classNamesString.size();
         for (size_t i = 0; i < classNameCount; ++i) {
@@ -707,8 +715,15 @@
             classNames.append('.');
             classNames.append(className);
         }
+    }
+    if (node->isPseudoElement()) {
+        if (node->pseudoId() == BEFORE)
+            classNames.appendLiteral("::before");
+        else if (node->pseudoId() == AFTER)
+            classNames.appendLiteral("::after");
+    }
+    if (!classNames.isEmpty())
         elementData->setClassName(classNames.toString());
-    }
 
     RenderElement* renderer = element.renderer();
     if (!renderer)

Modified: branches/safari-601.1.46-branch/Source/WebCore/style/StyleResolveTree.cpp (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebCore/style/StyleResolveTree.cpp	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebCore/style/StyleResolveTree.cpp	2015-12-04 20:51:49 UTC (rev 193441)
@@ -33,6 +33,7 @@
 #include "ElementRareData.h"
 #include "FlowThreadController.h"
 #include "InsertionPoint.h"
+#include "InspectorInstrumentation.h"
 #include "LoaderStrategy.h"
 #include "MainFrame.h"
 #include "NodeRenderStyle.h"
@@ -464,6 +465,7 @@
     if (!needsPseudoElement(current, pseudoId))
         return;
     Ref<PseudoElement> pseudoElement = PseudoElement::create(current, pseudoId);
+    InspectorInstrumentation::pseudoElementCreated(pseudoElement->document().page(), pseudoElement.get());
     setBeforeOrAfterPseudoElement(current, pseudoElement.copyRef(), pseudoId);
     attachRenderTree(pseudoElement.get(), *current.renderStyle(), renderTreePosition, nullptr);
 }

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/ChangeLog (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/ChangeLog	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/ChangeLog	2015-12-04 20:51:49 UTC (rev 193441)
@@ -1,5 +1,91 @@
 2015-12-04  Timothy Hatcher  <[email protected]>
 
+        Merge r187496. rdar://problem/23581597
+
+    2015-07-28  Joseph Pecoraro  <[email protected]>
+
+            Web Inspector: Show Pseudo Elements in DOM Tree
+            https://bugs.webkit.org/show_bug.cgi?id=139612
+
+            Reviewed by Timothy Hatcher.
+
+            * UserInterface/Controllers/DOMTreeManager.js:
+            (WebInspector.DOMTreeManager.prototype._pseudoElementAdded):
+            Hook up the new pseudo element DOMNode to the parent.
+
+            (WebInspector.DOMTreeManager.prototype._pseudoElementRemoved):
+            Unhook the pseudo element from its parent.
+
+            (WebInspector.DOMTreeManager.prototype._unbind):
+            When unbinding, unbind any pseudo element children we may have had.
+
+            * UserInterface/Models/DOMNode.js:
+            (WebInspector.DOMNode.prototype.isPseudoElement):
+            (WebInspector.DOMNode.prototype.pseudoType):
+            (WebInspector.DOMNode.prototype.hasPseudoElements):
+            (WebInspector.DOMNode.prototype.pseudoElements):
+            New state of a DOMNode may include pseudo elements.
+
+            (WebInspector.DOMNode.prototype.appropriateSelectorFor):
+            A selector for this node includes the selector for the node above it.
+
+            * UserInterface/Protocol/DOMObserver.js:
+            (WebInspector.DOMObserver.prototype.pseudoElementAdded):
+            (WebInspector.DOMObserver.prototype.pseudoElementRemoved):
+            Pass the message on to DOMTreeManager.
+
+            * UserInterface/Views/DOMTreeElement.js:
+            (WebInspector.DOMTreeElement.prototype.get editable):
+            Pseudo element nodes are not editable.
+
+            (WebInspector.DOMTreeElement.prototype.showChildNode):
+            (WebInspector.DOMTreeElement.prototype.onpopulate):
+            (WebInspector.DOMTreeElement.prototype.updateChildren):
+            (WebInspector.DOMTreeElement.prototype._nodeTitleInfo):
+            (WebInspector.DOMTreeElement.prototype._singleTextChild):
+            (WebInspector.DOMTreeElement.prototype._hasVisibleChildren):
+            (WebInspector.DOMTreeElement.prototype._visibleChildren):
+            (WebInspector.DOMTreeElement.prototype._updateChildren):
+            (WebInspector.DOMTreeElement.prototype.adjustCollapsedRange):
+            (WebInspector.DOMTreeElement.prototype.handleLoadAllChildren):
+            A DOMTreeElement's children are no longer 1-to-1 to DOMNode's children.
+            Instead a DOMNode may have a before/after pseudo element child that
+            are not included in the children list. Update parts of DOMTreeElement
+            to respect this list of visible children.
+
+            * UserInterface/Views/DOMTreeElementPathComponent.js:
+            (WebInspector.DOMTreeElementPathComponent):
+            * UserInterface/Views/PathComponentIcons.css:
+            (.dom-pseudo-element-icon .icon):
+            Styling for the path component when a pseudo element is selected.
+
+            * UserInterface/Views/DOMTreeOutline.css:
+            (.dom-tree-outline .html-pseudo-element):
+            (.dom-tree-outline .html-fragment.shadow):
+            (.webkit-html-fragment.shadow): Deleted.
+            Styles for pseudo elements in the DOM tree.
+
+            * UserInterface/Views/DOMTreeOutline.js:
+            (WebInspector.DOMTreeOutline.prototype._hideElement):
+            Make the hide element selector hide the host element.
+
+            * UserInterface/Views/CSSStyleDetailsSidebarPanel.js:
+            (WebInspector.CSSStyleDetailsSidebarPanel.prototype.addEventListeners):
+            (WebInspector.CSSStyleDetailsSidebarPanel.prototype.removeEventListeners):
+            (WebInspector.CSSStyleDetailsSidebarPanel.prototype._forcedPseudoClassCheckboxChanged):
+            (WebInspector.CSSStyleDetailsSidebarPanel.prototype._updatePseudoClassCheckboxes):
+            Pseudo class changes won't happen on pseudo elements, but will
+            happen on their host element, so listen to and make pseudo class
+            changes to the host element.
+
+            * UserInterface/Views/RulesStyleDetailsPanel.css:
+            (.sidebar > .panel.details.css-style .rules > *:first-child:matches(.new-rule)):
+            Since a pseudo element does not have a style attribute,
+            give some margin in the style sidebar above the "New Rule"
+            button so it looks better.
+
+2015-12-04  Timothy Hatcher  <[email protected]>
+
         Merge r187249. rdar://problem/23581597
 
     2015-07-23  Devin Rousso  <[email protected]>

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -246,16 +246,55 @@
         var node = this._idToDOMNode[nodeId];
         parent._removeChild(node);
         this._unbind(node);
-        this.dispatchEventToListeners(WebInspector.DOMTreeManager.Event.NodeRemoved, {node:node, parent});
+        this.dispatchEventToListeners(WebInspector.DOMTreeManager.Event.NodeRemoved, {node, parent});
     }
 
+    _pseudoElementAdded(parentId, pseudoElement)
+    {
+        var parent = this._idToDOMNode[parentId];
+        if (!parent)
+            return;
+
+        var node = new WebInspector.DOMNode(this, parent.ownerDocument, false, pseudoElement);
+        node.parentNode = parent;
+        this._idToDOMNode[node.id] = node;
+        console.assert(!parent.pseudoElements().get(node.pseudoType()));
+        parent.pseudoElements().set(node.pseudoType(), node);
+        this.dispatchEventToListeners(WebInspector.DOMTreeManager.Event.NodeInserted, {node, parent});
+    }
+
+    _pseudoElementRemoved(parentId, pseudoElementId)
+    {
+        var pseudoElement = this._idToDOMNode[pseudoElementId];
+        if (!pseudoElement)
+            return;
+
+        var parent = pseudoElement.parentNode;
+        console.assert(parent);
+        console.assert(parent.id === parentId);
+        if (!parent)
+            return;
+
+        parent._removeChild(pseudoElement);
+        this._unbind(pseudoElement);
+        this.dispatchEventToListeners(WebInspector.DOMTreeManager.Event.NodeRemoved, {node: pseudoElement, parent});
+    }
+
     _unbind(node)
     {
         this._removeContentNodeFromFlowIfNeeded(node);
 
         delete this._idToDOMNode[node.id];
+
         for (var i = 0; node.children && i < node.children.length; ++i)
             this._unbind(node.children[i]);
+
+        var pseudoElements = node.pseudoElements();
+        for (var pseudoElement of pseudoElements)
+            this._unbind(pseudoElement);
+
+        // FIXME: Handle shadow roots.
+        // FIXME: Handle template content.
     }
 
     get restoreSelectedNodeIsAllowed()

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Models/DOMNode.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Models/DOMNode.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Models/DOMNode.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -46,6 +46,7 @@
         this._nodeName = payload.nodeName;
         this._localName = payload.localName;
         this._nodeValue = payload.nodeValue;
+        this._pseudoType = payload.pseudoType;
         this._computedRole = payload.role;
 
         if (this._nodeType === Node.DOCUMENT_NODE)
@@ -78,9 +79,20 @@
             }
         }
 
+        // FIXME: Handle templateContent.
+
         if (payload.children)
             this._setChildrenPayload(payload.children);
 
+        this._pseudoElements = new Map;
+        if (payload.pseudoElements) {
+            for (var i = 0; i < payload.pseudoElements.length; ++i) {
+                var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload.pseudoElements[i]);
+                node.parentNode = this;
+                this._pseudoElements.set(node.pseudoType(), node);
+            }
+        }
+
         if (payload.contentDocument) {
             this._contentDocument = new WebInspector.DOMNode(domAgent, null, false, payload.contentDocument);
             this._children = [this._contentDocument];
@@ -221,6 +233,11 @@
         return this._isInShadowTree;
     }
 
+    isPseudoElement()
+    {
+        return this._pseudoType !== undefined;
+    }
+
     nodeType()
     {
         return this._nodeType;
@@ -246,6 +263,31 @@
         return this._localName;
     }
 
+    pseudoType()
+    {
+        return this._pseudoType;
+    }
+
+    hasPseudoElements()
+    {
+        return this._pseudoElements.size > 0;
+    }
+
+    pseudoElements()
+    {
+        return this._pseudoElements;
+    }
+
+    beforePseudoElement()
+    {
+        return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementType.Before) || null;
+    }
+
+    afterPseudoElement()
+    {
+        return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementType.After) || null;
+    }
+
     nodeValue()
     {
         return this._nodeValue;
@@ -407,6 +449,9 @@
 
     appropriateSelectorFor(justSelector)
     {
+        if (this.isPseudoElement())
+            return this.parentNode.appropriateSelectorFor() + "::" + this._pseudoType;
+
         var lowerCaseName = this.localName() || this.nodeName().toLowerCase();
 
         var id = this.getAttribute("id");
@@ -475,7 +520,12 @@
 
     _removeChild(node)
     {
-        this._children.splice(this._children.indexOf(node), 1);
+        // FIXME: Handle removal if this is a shadow root.
+        if (node.isPseudoElement())
+            this._pseudoElements.delete(node.pseudoType());
+        else
+            this._children.splice(this._children.indexOf(node), 1);
+
         node.parentNode = null;
         this._renumber();
     }
@@ -488,8 +538,7 @@
 
         this._children = this._shadowRoots.slice();
         for (var i = 0; i < payloads.length; ++i) {
-            var payload = payloads[i];
-            var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload);
+            var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payloads[i]);
             this._children.push(node);
         }
         this._renumber();
@@ -597,3 +646,8 @@
     AttributeModified: "dom-node-attribute-modified",
     AttributeRemoved: "dom-node-attribute-removed"
 };
+
+WebInspector.DOMNode.PseudoElementType = {
+    Before: "before",
+    After: "after",
+};

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -81,4 +81,14 @@
     {
         WebInspector.domTreeManager._childNodeRemoved(parentNodeId, nodeId);
     }
+
+    pseudoElementAdded(parentNodeId, pseudoElement)
+    {
+        WebInspector.domTreeManager._pseudoElementAdded(parentNodeId, pseudoElement);
+    }
+
+    pseudoElementRemoved(parentNodeId, pseudoElementId)
+    {
+        WebInspector.domTreeManager._pseudoElementRemoved(parentNodeId, pseudoElementId);
+    }
 };

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/CSSStyleDetailsSidebarPanel.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/CSSStyleDetailsSidebarPanel.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/CSSStyleDetailsSidebarPanel.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -157,12 +157,20 @@
 
     addEventListeners()
     {
-        this.domNode.addEventListener(WebInspector.DOMNode.Event.EnabledPseudoClassesChanged, this._updatePseudoClassCheckboxes, this);
+        var effectiveDOMNode = this.domNode.isPseudoElement() ? this.domNode.parentNode : this.domNode;
+        if (!effectiveDOMNode)
+            return;
+
+        effectiveDOMNode.addEventListener(WebInspector.DOMNode.Event.EnabledPseudoClassesChanged, this._updatePseudoClassCheckboxes, this);
     }
 
     removeEventListeners()
     {
-        this.domNode.removeEventListener(null, null, this);
+        var effectiveDOMNode = this.domNode.isPseudoElement() ? this.domNode.parentNode : this.domNode;
+        if (!effectiveDOMNode)
+            return;
+
+        effectiveDOMNode.removeEventListener(null, null, this);
     }
 
     // Private
@@ -235,7 +243,9 @@
         if (!this.domNode)
             return;
 
-        this.domNode.setPseudoClassEnabled(pseudoClass, event.target.checked);
+        var effectiveDOMNode = this.domNode.isPseudoElement() ? this.domNode.parentNode : this.domNode;
+
+        effectiveDOMNode.setPseudoClassEnabled(pseudoClass, event.target.checked);
     }
 
     _updatePseudoClassCheckboxes()
@@ -243,8 +253,10 @@
         if (!this.domNode)
             return;
 
-        var enabledPseudoClasses = this.domNode.enabledPseudoClasses;
+        var effectiveDOMNode = this.domNode.isPseudoElement() ? this.domNode.parentNode : this.domNode;
 
+        var enabledPseudoClasses = effectiveDOMNode.enabledPseudoClasses;
+
         for (var pseudoClass in this._forcedPseudoClassCheckboxes) {
             var checkboxElement = this._forcedPseudoClassCheckboxes[pseudoClass];
             checkboxElement.checked = enabledPseudoClasses.includes(pseudoClass);

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -35,7 +35,7 @@
         super("", node);
 
         this._elementCloseTag = elementCloseTag;
-        this.hasChildren = !elementCloseTag && node.hasChildNodes() && !this._showInlineText(node);
+        this.hasChildren = !elementCloseTag && this._hasVisibleChildren();
 
         if (this.representedObject.nodeType() === Node.ELEMENT_NODE && !elementCloseTag)
             this._canAddAttributes = true;
@@ -159,8 +159,11 @@
 
     get editable()
     {
-        if (this.representedObject.isInShadowTree())
+        var node = this.representedObject;
+        if (node.isInShadowTree())
             return false;
+        if (node.isPseudoElement())
+            return false;
 
         return this.treeOutline.editable;
     }
@@ -190,19 +193,22 @@
         return count;
     }
 
-    showChild(index)
+    showChildNode(node)
     {
         console.assert(!this._elementCloseTag);
         if (this._elementCloseTag)
-            return false;
+            return null;
 
+        var index = this._visibleChildren().indexOf(node);
+        if (index === -1)
+            return null;
+
         if (index >= this.expandedChildrenLimit) {
             this._expandedChildrenLimit = index + 1;
             this._updateChildren(true);
         }
 
-        // Whether index-th child is visible in the children tree
-        return this.expandedChildCount > index;
+        return this.children[index];
     }
 
     _createTooltipForNode()
@@ -285,7 +291,7 @@
 
     onpopulate()
     {
-        if (this.children.length || this._showInlineText(this.representedObject) || this._elementCloseTag)
+        if (this.children.length || !this._hasVisibleChildren() || this._elementCloseTag)
             return;
 
         this.updateChildren();
@@ -300,6 +306,7 @@
     {
         if (this._elementCloseTag)
             return;
+
         this.representedObject.getChildNodes(this._updateChildren.bind(this, fullRefresh));
     }
 
@@ -326,78 +333,85 @@
             return;
 
         this._updateChildrenInProgress = true;
+
+        var node = this.representedObject;
         var selectedNode = this.treeOutline.selectedDOMNode();
         var originalScrollTop = 0;
-        if (fullRefresh) {
+
+        var hasVisibleChildren = this._hasVisibleChildren();
+
+        if (fullRefresh || !hasVisibleChildren) {
             var treeOutlineContainerElement = this.treeOutline.element.parentNode;
             originalScrollTop = treeOutlineContainerElement.scrollTop;
             var selectedTreeElement = this.treeOutline.selectedTreeElement;
             if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
                 this.select();
             this.removeChildren();
-        }
 
-        var treeElement = this;
-        var treeChildIndex = 0;
-        var elementToSelect;
-
-        function updateChildrenOfNode(node)
-        {
-            var treeOutline = treeElement.treeOutline;
-            var child = node.firstChild;
-            while (child) {
-                var currentTreeElement = treeElement.children[treeChildIndex];
-                if (!currentTreeElement || currentTreeElement.representedObject !== child) {
-                    // Find any existing element that is later in the children list.
-                    var existingTreeElement = null;
-                    for (var i = (treeChildIndex + 1), size = treeElement.expandedChildCount; i < size; ++i) {
-                        if (treeElement.children[i].representedObject === child) {
-                            existingTreeElement = treeElement.children[i];
-                            break;
-                        }
-                    }
-
-                    if (existingTreeElement && existingTreeElement.parent === treeElement) {
-                        // If an existing element was found and it has the same parent, just move it.
-                        treeElement.moveChild(existingTreeElement, treeChildIndex);
-                    } else {
-                        // No existing element found, insert a new element.
-                        if (treeChildIndex < treeElement.expandedChildrenLimit) {
-                            var newElement = treeElement.insertChildElement(child, treeChildIndex);
-                            if (child === selectedNode)
-                                elementToSelect = newElement;
-                            if (treeElement.expandedChildCount > treeElement.expandedChildrenLimit)
-                                treeElement.expandedChildrenLimit++;
-                        }
-                    }
-                }
-
-                child = child.nextSibling;
-                ++treeChildIndex;
+            // No longer have children.
+            if (!hasVisibleChildren) {
+                this.hasChildren = false;
+                this.updateTitle();
+                this._updateChildrenInProgress = false;
+                return;
             }
         }
 
+        // We now have children.
+        if (!this.hasChildren) {
+            this.hasChildren = true;
+            this.updateTitle();
+        }
+
         // Remove any tree elements that no longer have this node (or this node's contentDocument) as their parent.
+        // Keep a list of existing tree elements for nodes that we can use later.
+        var existingChildTreeElements = new Map;
         for (var i = (this.children.length - 1); i >= 0; --i) {
-            var currentChild = this.children[i];
-            var currentNode = currentChild.representedObject;
+            var currentChildTreeElement = this.children[i];
+            var currentNode = currentChildTreeElement.representedObject;
             var currentParentNode = currentNode.parentNode;
-
-            if (currentParentNode === this.representedObject)
+            if (currentParentNode === node) {
+                existingChildTreeElements.set(currentNode, currentChildTreeElement);
                 continue;
+            }
 
             var selectedTreeElement = this.treeOutline.selectedTreeElement;
-            if (selectedTreeElement && (selectedTreeElement === currentChild || selectedTreeElement.hasAncestor(currentChild)))
+            if (selectedTreeElement && (selectedTreeElement === currentChildTreeElement || selectedTreeElement.hasAncestor(currentChildTreeElement)))
                 this.select();
 
             this.removeChildAtIndex(i);
         }
 
-        updateChildrenOfNode(this.representedObject);
+        // Move / create TreeElements for our visible children.
+        var childIndex = 0;
+        var elementToSelect = null;
+        var visibleChildren = this._visibleChildren();
+        for (var i = 0; i < visibleChildren.length && i < this.expandedChildrenLimit; ++i) {
+            var childNode = visibleChildren[i];
+
+            // Already have a tree element for this child, just move it.
+            var existingChildTreeElement = existingChildTreeElements.get(childNode);
+            if (existingChildTreeElement) {
+                this.moveChild(existingChildTreeElement, i);
+                continue;
+            }
+
+            // No existing tree element for this child. Insert a new element.
+            var newChildTreeElement = this.insertChildElement(childNode, i);
+
+            // Update state.
+            if (childNode === selectedNode)
+                elementToSelect = newChildTreeElement;
+            if (this.expandedChildCount > this.expandedChildrenLimit)
+                this.expandedChildrenLimit++;
+        }
+
+        // Update expand all children button.
         this.adjustCollapsedRange();
 
+        // Insert closing tag tree element.
         var lastChild = this.children.lastValue;
-        if (this.representedObject.nodeType() === Node.ELEMENT_NODE && (!lastChild || !lastChild._elementCloseTag))
+        if (node.nodeType() === Node.ELEMENT_NODE && (!lastChild || !lastChild._elementCloseTag))
             this.insertChildElement(this.representedObject, this.children.length, true);
 
         // We want to restore the original selection and tree scroll position after a full refresh, if possible.
@@ -418,16 +432,18 @@
             this.removeChild(this.expandAllButtonElement.__treeElement);
 
         var node = this.representedObject;
-        if (!node.children)
+        if (!this._hasVisibleChildren())
             return;
-        var childNodeCount = node.children.length;
 
+        var visibleChildren = this._visibleChildren();
+        var totalChildrenCount = visibleChildren.length;
+
         // In case some nodes from the expanded range were removed, pull some nodes from the collapsed range into the expanded range at the bottom.
-        for (var i = this.expandedChildCount, limit = Math.min(this.expandedChildrenLimit, childNodeCount); i < limit; ++i)
-            this.insertChildElement(node.children[i], i);
+        for (var i = this.expandedChildCount, limit = Math.min(this.expandedChildrenLimit, totalChildrenCount); i < limit; ++i)
+            this.insertChildElement(totalChildrenCount[i], i);
 
         var expandedChildCount = this.expandedChildCount;
-        if (childNodeCount > this.expandedChildCount) {
+        if (totalChildrenCount > this.expandedChildCount) {
             var targetButtonIndex = expandedChildCount;
             if (!this.expandAllButtonElement) {
                 var button = document.createElement("button");
@@ -445,14 +461,16 @@
             } else if (!this.expandAllButtonElement.__treeElement.parent)
                 this.insertChild(this.expandAllButtonElement.__treeElement, targetButtonIndex);
 
-            this.expandAllButtonElement.textContent = WebInspector.UIString("Show All Nodes (%d More)").format(childNodeCount - expandedChildCount);
+            this.expandAllButtonElement.textContent = WebInspector.UIString("Show All Nodes (%d More)").format(totalChildrenCount - expandedChildCount);
         } else if (this.expandAllButtonElement)
             this.expandAllButtonElement = null;
     }
 
     handleLoadAllChildren()
     {
-        this.expandedChildrenLimit = Math.max(this.representedObject.childNodeCount, this.expandedChildrenLimit + WebInspector.DOMTreeElement.InitialChildrenLimit);
+        var visibleChildren = this._visibleChildren();
+        var totalChildrenCount = visibleChildren.length;
+        this.expandedChildrenLimit = Math.max(visibleChildren.length, this.expandedChildrenLimit + WebInspector.DOMTreeElement.InitialChildrenLimit);
     }
 
     onexpand()
@@ -575,7 +593,7 @@
         if (this.treeOutline.selectedDOMNode() !== this.representedObject)
             return false;
 
-        if (this.representedObject.isInShadowTree())
+        if (this.representedObject.isInShadowTree() || this.representedObject.isPseudoElement())
             return false;
 
         if (this.representedObject.nodeType() !== Node.ELEMENT_NODE && this.representedObject.nodeType() !== Node.TEXT_NODE)
@@ -1114,7 +1132,7 @@
 
         switch (node.nodeType()) {
             case Node.DOCUMENT_FRAGMENT_NODE:
-                var fragmentElement = info.titleDOM.createChild("span", "webkit-html-fragment");
+                var fragmentElement = info.titleDOM.createChild("span", "html-fragment");
                 if (node.isInShadowTree()) {
                     fragmentElement.textContent = WebInspector.UIString("Shadow Content");
                     fragmentElement.classList.add("shadow");
@@ -1128,6 +1146,14 @@
                 break;
 
             case Node.ELEMENT_NODE:
+                if (node.isPseudoElement()) {
+                    var pseudoElement = info.titleDOM.createChild("span", "html-pseudo-element");
+                    pseudoElement.textContent = "::" + node.pseudoType();
+                    info.titleDOM.appendChild(document.createTextNode("\u200B"));
+                    info.hasChildren = false;
+                    break;
+                }
+
                 var tagName = node.nodeNameInCorrectCase();
                 if (this._elementCloseTag) {
                     this._buildTagDOM(info.titleDOM, tagName, true, true);
@@ -1237,6 +1263,8 @@
 
         if (node.hasShadowRoots())
             return null;
+        if (node.hasPseudoElements())
+            return null;
 
         var sibling = firstChild.nextSibling;
         return sibling ? null : firstChild;
@@ -1252,6 +1280,41 @@
         return false;
     }
 
+    _hasVisibleChildren()
+    {
+        var node = this.representedObject;
+
+        if (this._showInlineText(node))
+            return false;
+
+        if (node.hasChildNodes())
+            return true;
+        if (node.hasPseudoElements())
+            return true;
+
+        return false;
+    }
+
+    _visibleChildren()
+    {
+        var node = this.representedObject;
+
+        var visibleChildren = [];
+
+        var beforePseudoElement = node.beforePseudoElement();
+        if (beforePseudoElement)
+            visibleChildren.push(beforePseudoElement);
+
+        if (node.childNodeCount)
+            visibleChildren = visibleChildren.concat(node.children);
+
+        var afterPseudoElement = node.afterPseudoElement();
+        if (afterPseudoElement)
+            visibleChildren.push(afterPseudoElement);
+
+        return visibleChildren;
+    }
+
     remove()
     {
         var parentElement = this.parent;

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeElementPathComponent.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeElementPathComponent.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeElementPathComponent.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -34,8 +34,13 @@
 
         switch (node.nodeType()) {
         case Node.ELEMENT_NODE:
-            className = WebInspector.DOMTreeElementPathComponent.DOMElementIconStyleClassName;
-            title = WebInspector.displayNameForNode(node);
+            if (node.isPseudoElement()) {
+                className = WebInspector.DOMTreeElementPathComponent.DOMPseudoElementIconStyleClassName;
+                title = "::" + node.pseudoType();
+            } else {
+                className = WebInspector.DOMTreeElementPathComponent.DOMElementIconStyleClassName;
+                title = WebInspector.displayNameForNode(node);
+            }
             break;
 
         case Node.TEXT_NODE:
@@ -129,6 +134,7 @@
 };
 
 WebInspector.DOMTreeElementPathComponent.DOMElementIconStyleClassName = "dom-element-icon";
+WebInspector.DOMTreeElementPathComponent.DOMPseudoElementIconStyleClassName = "dom-pseudo-element-icon";
 WebInspector.DOMTreeElementPathComponent.DOMTextNodeIconStyleClassName = "dom-text-node-icon";
 WebInspector.DOMTreeElementPathComponent.DOMCommentIconStyleClassName = "dom-comment-icon";
 WebInspector.DOMTreeElementPathComponent.DOMDocumentTypeIconStyleClassName = "dom-document-type-icon";

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.css (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.css	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.css	2015-12-04 20:51:49 UTC (rev 193441)
@@ -154,12 +154,16 @@
     white-space: pre-wrap;
 }
 
+.dom-tree-outline .html-pseudo-element {
+    color: hsl(0, 59%, 41%);
+}
+
+.dom-tree-outline .html-fragment.shadow {
+    opacity: 0.6;
+}
+
 .showing-find-banner .dom-tree-outline .search-highlight {
     color: black;
     background-color: rgba(235, 215, 38, 0.2);
     border-bottom: 1px solid rgb(237, 202, 71);
 }
-
-.webkit-html-fragment.shadow {
-    opacity: 0.6;
-}

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js	2015-12-04 20:51:49 UTC (rev 193441)
@@ -228,14 +228,15 @@
         var treeElement = this.findTreeElement(node);
         if (treeElement)
             return treeElement;
+
         if (!node.parentNode)
             return null;
 
         treeElement = this.createTreeElementFor(node.parentNode);
-        if (treeElement && treeElement.showChild(node.index))
-            return treeElement.children[node.index];
+        if (!treeElement)
+            return null;
 
-        return null;
+        return treeElement.showChildNode(node);
     }
 
     set suppressRevealAndSelect(x)
@@ -548,12 +549,19 @@
 
         event.preventDefault();
 
-        var selectedNode = this.selectedTreeElement.representedObject;
-        console.assert(selectedNode);
-        if (!selectedNode)
+        var effectiveNode = this.selectedTreeElement.representedObject;
+        console.assert(effectiveNode);
+        if (!effectiveNode)
             return;
 
-        if (selectedNode.nodeType() !== Node.ELEMENT_NODE)
+        if (effectiveNode.isPseudoElement()) {
+            effectiveNode = effectiveNode.parentNode;
+            console.assert(effectiveNode);
+            if (!effectiveNode)
+                return;
+        }            
+
+        if (effectiveNode.nodeType() !== Node.ELEMENT_NODE)
             return;
 
         function resolvedNode(object)
@@ -579,7 +587,7 @@
             object.release();
         }
 
-        WebInspector.RemoteObject.resolveNode(selectedNode, "", resolvedNode);
+        WebInspector.RemoteObject.resolveNode(effectiveNode, "", resolvedNode);
     }
 };
 

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/PathComponentIcons.css (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/PathComponentIcons.css	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/PathComponentIcons.css	2015-12-04 20:51:49 UTC (rev 193441)
@@ -35,6 +35,10 @@
     content: url(../Images/DOMElement.svg);
 }
 
+.dom-pseudo-element-icon .icon {
+    content: url(../Images/PseudoElement.svg);
+}
+
 .dom-text-node-icon .icon {
     content: url(../Images/DOMTextNode.svg);
 }

Modified: branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.css (193440 => 193441)


--- branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.css	2015-12-04 20:51:31 UTC (rev 193440)
+++ branches/safari-601.1.46-branch/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.css	2015-12-04 20:51:49 UTC (rev 193441)
@@ -52,6 +52,10 @@
     opacity: 0.5;
 }
 
+.sidebar > .panel.details.css-style .rules > *:first-child:matches(.new-rule) {
+    margin-top: 8px;
+}
+
 .sidebar > .panel.details.css-style > .content.filter-in-progress .label {
     padding-top: 15px;
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to