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;
}