Diff
Modified: trunk/LayoutTests/ChangeLog (145056 => 145057)
--- trunk/LayoutTests/ChangeLog 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/LayoutTests/ChangeLog 2013-03-07 10:39:15 UTC (rev 145057)
@@ -1,3 +1,15 @@
+2013-03-07 Antoine Quint <grao...@apple.com>
+
+ Web Inspector: identify layers for CSS generated content in LayerTreeAgent
+ https://bugs.webkit.org/show_bug.cgi?id=111551
+
+ New tests for layers associated with pseudo elements.
+
+ Reviewed by Simon Fraser.
+
+ * inspector-protocol/layers/layers-generated-content-expected.txt: Added.
+ * inspector-protocol/layers/layers-generated-content.html: Added.
+
2013-03-07 Ádám Kallai <ka...@inf.u-szeged.hu>
[Qt] Unreviewed gardneing. Skipped some failing tests.
Added: trunk/LayoutTests/inspector-protocol/layers/layers-generated-content-expected.txt (0 => 145057)
--- trunk/LayoutTests/inspector-protocol/layers/layers-generated-content-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector-protocol/layers/layers-generated-content-expected.txt 2013-03-07 10:39:15 UTC (rev 145057)
@@ -0,0 +1,33 @@
+
+=== Enable the LayerTree agent ===
+
+PASS
+
+=== Get the Document ===
+
+PASS
+
+=== Get the layer tree ===
+
+PASS
+
+=== Check layers ===
+
+PASS: Expected number of ::before layers.
+PASS: The sole ::before layer has a pseudo element.
+PASS: Expected number of ::after layers.
+PASS: The first ::after layer has a pseudo element.
+PASS: The second ::after layer has a pseudo element.
+PASS: The second ::after layer is a reflection.
+PASS: The two ::after layers have the same node id.
+PASS: The two ::after layers have the same pseudo element id.
+PASS: The non-reflection generated generated layers have the same node id.
+PASS: The non-reflection generated layers have different pseudo element ids.
+
+=== Check generating node ===
+
+PASS: Generating node was found.
+PASS: Generating node has expected localName.
+PASS: Generating node has id.
+PASS: Generating node has expected id.
+
Added: trunk/LayoutTests/inspector-protocol/layers/layers-generated-content.html (0 => 145057)
--- trunk/LayoutTests/inspector-protocol/layers/layers-generated-content.html (rev 0)
+++ trunk/LayoutTests/inspector-protocol/layers/layers-generated-content.html 2013-03-07 10:39:15 UTC (rev 145057)
@@ -0,0 +1,166 @@
+<html>
+<head>
+<script type="text/_javascript_" src=""
+<script type="text/_javascript_">
+
+function test()
+{
+
+ var nodes;
+
+ InspectorTest.eventHandler["DOM.setChildNodes"] = setChildNodes;
+
+ enableLayerTreeAgent();
+
+ function enableLayerTreeAgent(result)
+ {
+ step({
+ name: "Enable the LayerTree agent",
+ command: "LayerTree.enable",
+ parameters: {},
+ callback: getDocument
+ });
+ };
+
+ function getDocument()
+ {
+ // We must first get the document so that later on we may get sensible nodeIds.
+ step({
+ name: "Get the Document",
+ command: "DOM.getDocument",
+ parameters: {},
+ callback: getLayerTree
+ });
+ };
+
+ function getLayerTree(result)
+ {
+ step({
+ name: "Get the layer tree",
+ command: "LayerTree.layersForNode",
+ parameters: {"nodeId": result.root.nodeId},
+ callback: gotLayerTree
+ });
+ };
+
+ function gotLayerTree(result)
+ {
+ var beforeLayers = [];
+ var afterLayers = [];
+ result.layers.forEach(function (layer) {
+ if (!layer.isGeneratedContent)
+ return;
+
+ if (layer.pseudoClass === "before")
+ beforeLayers.push(layer);
+ if (layer.pseudoClass === "after")
+ afterLayers.push(layer);
+ });
+
+ logTestName("Check layers");
+
+ assert("Expected number of ::before layers", beforeLayers.length, 1);
+ assert("The sole ::before layer has a pseudo element", beforeLayers[0].pseudoElementId !== "", true);
+
+ assert("Expected number of ::after layers", afterLayers.length, 2);
+ assert("The first ::after layer has a pseudo element", afterLayers[0].pseudoElementId !== "", true);
+ assert("The second ::after layer has a pseudo element", afterLayers[1].pseudoElementId !== "", true);
+ assert("The second ::after layer is a reflection", afterLayers[1].isReflection, true);
+ assert("The two ::after layers have the same node id", afterLayers[0].nodeId, afterLayers[1].nodeId);
+ assert("The two ::after layers have the same pseudo element id", afterLayers[0].pseudoElementId, afterLayers[1].pseudoElementId);
+
+ assert("The non-reflection generated generated layers have the same node id", beforeLayers[0].nodeId, afterLayers[0].nodeId);
+ assert("The non-reflection generated layers have different pseudo element ids", beforeLayers[0].pseudoElementId !== afterLayers[0].pseudoElementId, true);
+
+ var node;
+ var nodeId = beforeLayers[0].nodeId;
+ for (var i = 0, count = nodes.length; i < count; ++i) {
+ if (nodes[i].nodeId === nodeId) {
+ node = nodes[i];
+ break;
+ }
+ }
+
+ logTestName("Check generating node");
+
+ assert("Generating node was found", !!node, true);
+ assert("Generating node has expected localName", node.localName, "div");
+ assert("Generating node has id", node.attributes[0], "id");
+ assert("Generating node has expected id", node.attributes[1], "generator");
+
+ InspectorTest.completeTest();
+ };
+
+ function setChildNodes (messageObject) {
+ nodes = messageObject.params.nodes;
+ };
+
+ function step(test)
+ {
+ if (test.callback)
+ logTestName(test.name);
+ runCommand(test);
+ };
+
+ function logTestName(name)
+ {
+ InspectorTest.log("\n=== " + name + " ===\n");
+ };
+
+ function runCommand(command)
+ {
+ InspectorTest.sendCommand(command.command, command.parameters, function(messageObject) {
+ if (messageObject.hasOwnProperty("error")) {
+ InspectorTest.log("FAIL: " + messageObject.error.message + " (" + messageObject.error.code + ")");
+ InspectorTest.completeTest();
+ return;
+ }
+
+ if (command.name)
+ InspectorTest.log("PASS");
+
+ if (command.callback)
+ command.callback(messageObject.result);
+ });
+ };
+
+ function assert(name, actual, expected)
+ {
+ if (expected === actual)
+ InspectorTest.log("PASS: " + name + ".");
+ else
+ InspectorTest.log("FAIL: " + name + ". Expected " + expected + " but got " + actual);
+ };
+
+};
+
+window.addEventListener("DOMContentLoaded", function()
+{
+ runTest();
+}, false);
+
+</script>
+<style type="text/css">
+
+ #generator::before,
+ #generator::after {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background-color: black;
+ -webkit-transform: translateZ(0);
+ content: "";
+ }
+
+ #generator::after {
+ -webkit-box-reflect:below 1px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0.5, transparent), to(white));
+ }
+
+</style>
+</head>
+<body>
+
+ <div id="generator"></div>
+
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (145056 => 145057)
--- trunk/Source/WebCore/ChangeLog 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/ChangeLog 2013-03-07 10:39:15 UTC (rev 145057)
@@ -1,3 +1,79 @@
+2013-03-07 Antoine Quint <grao...@apple.com>
+
+ Web Inspector: identify layers for CSS generated content in LayerTreeAgent
+ https://bugs.webkit.org/show_bug.cgi?id=111551
+
+ Layers can be associated with pseudo elements, elements that are generated
+ when ::before or ::after pseudo-classes are used to style an element and
+ provide additional rendering using the CSS "content" property. In order to
+ identify that a layer is associated to a pseudo element, we add a new set of
+ optional properties on the Layer object: "isGeneratedContent", "pseudoElementId"
+ and "pseudoClass".
+
+ We need to provide the "pseudoElementId" for the same reason we provide a
+ "nodeId" for the Layer. This is so that the front-end can identify that a
+ layer whose "layerId" may have changed between two layer tree updates is
+ used to render content for the same pseudo element.
+
+ In the instance that a layer is rendering generated content, we also set
+ the "nodeId" to be the generating node's id. The generating node is the
+ node in the DOM that the user styled using ::before or ::after pseudo-classes.
+ We also add the pseudo-class used as the "pseudoClass" property such that the
+ front-end may provide useful information to the user about what type of
+ pseudo-class yielded the generation of this layer. We also deal with the
+ possibility that the layer is a reflection for a pseudo element.
+
+ Since pseudo elements are not part of the user-visible DOM, and thus can't be
+ pushed to the front-end, we maintain a special ID hash map in the LayerTreeAgent.
+ In order to be able to unbind a pseudo element id and a pseudo element, we add a
+ destructor method to PseudoElement and provide a new instrumentation call so that
+ the LayerTreeAgent may be notified of a PseudoElement being destroyed. This
+ reflects the principle we use to update the RenderLayer bindings when a
+ RenderLayer is destroyed, as notified by the RenderLayerCompositor.
+
+ Reviewed by Simon Fraser.
+
+ Test: inspector-protocol/layers/layers-generated-content.html
+
+ * dom/PseudoElement.cpp:
+ (WebCore::PseudoElement::~PseudoElement): New destructor used to notify
+ InspectorInstrumentation of a PseudoElement being destroyed, such that
+ it can eventually be relayed to the InspectorLayerTreeAgent.
+ (WebCore):
+ * dom/PseudoElement.h:
+ (PseudoElement):
+ * inspector/Inspector.json: Add the new PseudoElementId type used for
+ the new "pseudoElementId" property on Layer, to which we also add the
+ "isGeneratedContent" and "pseudoClass" properties.
+ * inspector/InspectorInstrumentation.cpp:
+ (WebCore):
+ (WebCore::InspectorInstrumentation::pseudoElementDestroyedImpl):
+ * inspector/InspectorInstrumentation.h:
+ (WebCore):
+ (InspectorInstrumentation):
+ (WebCore::InspectorInstrumentation::pseudoElementDestroyed): New method
+ called in the PseudoElement destructor used to relay the fact that a
+ PseudoElement was destroyed to the InspectorLayerTreeAgent.
+ * inspector/InspectorLayerTreeAgent.cpp:
+ (WebCore::InspectorLayerTreeAgent::reset): Clear the new PseudoElement
+ identifier maps.
+ (WebCore::InspectorLayerTreeAgent::pseudoElementDestroyed): Remove any
+ identifier binding for the provided PseudoElement being destroyed.
+ (WebCore):
+ (WebCore::InspectorLayerTreeAgent::buildObjectForLayer): Set the new
+ "isGeneratedContent", "pseudoElementId" and "pseudoClass" properties on
+ the Layer object for layers associated with a pseudo element.
+ (WebCore::InspectorLayerTreeAgent::bind): Use emptyString() instead of "".
+ (WebCore::InspectorLayerTreeAgent::unbind): Use an iterator instead of a
+ get() and remove() combination in order to reduce lookups and mimick the
+ code written for unbindPseudoElement().
+ (WebCore::InspectorLayerTreeAgent::bindPseudoElement): Binds the provided
+ PseudoElement to a unique identifier.
+ (WebCore::InspectorLayerTreeAgent::unbindPseudoElement): Unbinds the
+ provided PseudoElement from its unique identifier.
+ * inspector/InspectorLayerTreeAgent.h:
+ (InspectorLayerTreeAgent):
+
2013-03-07 Keishi Hattori <kei...@webkit.org>
Add clear button to date/time input types
Modified: trunk/Source/WebCore/dom/PseudoElement.cpp (145056 => 145057)
--- trunk/Source/WebCore/dom/PseudoElement.cpp 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/dom/PseudoElement.cpp 2013-03-07 10:39:15 UTC (rev 145057)
@@ -28,6 +28,7 @@
#include "PseudoElement.h"
#include "ContentData.h"
+#include "InspectorInstrumentation.h"
#include "NodeRenderingContext.h"
#include "RenderObject.h"
#include "RenderQuote.h"
@@ -63,6 +64,11 @@
setHasCustomStyleCallbacks();
}
+PseudoElement::~PseudoElement()
+{
+ InspectorInstrumentation::pseudoElementDestroyed(document()->page(), this);
+}
+
PassRefPtr<RenderStyle> PseudoElement::customStyleForRenderer()
{
return parentOrShadowHostElement()->renderer()->getCachedPseudoStyle(m_pseudoId);
Modified: trunk/Source/WebCore/dom/PseudoElement.h (145056 => 145057)
--- trunk/Source/WebCore/dom/PseudoElement.h 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/dom/PseudoElement.h 2013-03-07 10:39:15 UTC (rev 145057)
@@ -40,6 +40,7 @@
{
return adoptRef(new PseudoElement(parent, pseudoId));
}
+ ~PseudoElement();
virtual PassRefPtr<RenderStyle> customStyleForRenderer() OVERRIDE;
virtual void attach() OVERRIDE;
Modified: trunk/Source/WebCore/inspector/Inspector.json (145056 => 145057)
--- trunk/Source/WebCore/inspector/Inspector.json 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/inspector/Inspector.json 2013-03-07 10:39:15 UTC (rev 145057)
@@ -3646,6 +3646,11 @@
"description": "Unique RenderLayer identifier."
},
{
+ "id": "PseudoElementId",
+ "type": "string",
+ "description": "Unique PseudoElement identifier."
+ },
+ {
"id": "IntRect",
"type": "object",
"description": "A rectangle.",
@@ -3668,7 +3673,10 @@
{ "name": "memory", "type": "integer", "description": "Estimated memory used by this layer." },
{ "name": "compositedBounds", "$ref": "IntRect", "description": "The bounds of the composited layer." },
{ "name": "isInShadowTree", "type": "boolean", "optional": true, "description": "Indicates whether this layer is associated with an element hosted in a shadow tree." },
- { "name": "isReflection", "type": "boolean", "optional": true, "description": "Indicates whether this layer was used to provide a reflection for the element." }
+ { "name": "isReflection", "type": "boolean", "optional": true, "description": "Indicates whether this layer was used to provide a reflection for the element." },
+ { "name": "isGeneratedContent", "type": "boolean", "optional": true, "description": "Indicates whether the layer is attached to a pseudo element that is CSS generated content." },
+ { "name": "pseudoElementId", "$ref": "PseudoElementId", "optional": true, "description": "The id for the pseudo element associated with this layer." },
+ { "name": "pseudoClass", "type": "string", "optional": true, "description": "The name of the CSS pseudo-class that prompted the layer's content to be generated." }
]
}
],
Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp (145056 => 145057)
--- trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp 2013-03-07 10:39:15 UTC (rev 145057)
@@ -1367,6 +1367,12 @@
if (InspectorLayerTreeAgent* layerTreeAgent = instrumentingAgents->inspectorLayerTreeAgent())
layerTreeAgent->renderLayerDestroyed(renderLayer);
}
+
+void InspectorInstrumentation::pseudoElementDestroyedImpl(InstrumentingAgents* instrumentingAgents, PseudoElement* pseudoElement)
+{
+ if (InspectorLayerTreeAgent* layerTreeAgent = instrumentingAgents->inspectorLayerTreeAgent())
+ layerTreeAgent->pseudoElementDestroyed(pseudoElement);
+}
#endif
namespace InstrumentationEvents {
Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.h (145056 => 145057)
--- trunk/Source/WebCore/inspector/InspectorInstrumentation.h 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.h 2013-03-07 10:39:15 UTC (rev 145057)
@@ -70,6 +70,7 @@
class InstrumentingAgents;
class KURL;
class Node;
+class PseudoElement;
class RenderLayer;
class RenderObject;
class ResourceRequest;
@@ -310,6 +311,7 @@
#if USE(ACCELERATED_COMPOSITING)
static void layerTreeDidChange(Page*);
static void renderLayerDestroyed(Page*, const RenderLayer*);
+ static void pseudoElementDestroyed(Page*, PseudoElement*);
#endif
private:
@@ -496,6 +498,7 @@
#if USE(ACCELERATED_COMPOSITING)
static void layerTreeDidChangeImpl(InstrumentingAgents*);
static void renderLayerDestroyedImpl(InstrumentingAgents*, const RenderLayer*);
+ static void pseudoElementDestroyedImpl(InstrumentingAgents*, PseudoElement*);
#endif
static int s_frontendCounter;
@@ -2015,7 +2018,18 @@
UNUSED_PARAM(renderLayer);
#endif
}
+
+inline void InspectorInstrumentation::pseudoElementDestroyed(Page* page, PseudoElement* pseudoElement)
+{
+#if ENABLE(INSPECTOR)
+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page))
+ pseudoElementDestroyedImpl(instrumentingAgents, pseudoElement);
+#else
+ UNUSED_PARAM(page);
+ UNUSED_PARAM(pseudoElement);
#endif
+}
+#endif
#if ENABLE(INSPECTOR)
inline bool InspectorInstrumentation::collectingHTMLParseErrors(Page* page)
Modified: trunk/Source/WebCore/inspector/InspectorLayerTreeAgent.cpp (145056 => 145057)
--- trunk/Source/WebCore/inspector/InspectorLayerTreeAgent.cpp 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/inspector/InspectorLayerTreeAgent.cpp 2013-03-07 10:39:15 UTC (rev 145057)
@@ -41,6 +41,7 @@
#include "InspectorState.h"
#include "InstrumentingAgents.h"
#include "IntRect.h"
+#include "PseudoElement.h"
#include "RenderLayer.h"
#include "RenderLayerBacking.h"
#include "RenderView.h"
@@ -83,6 +84,8 @@
{
m_documentLayerToIdMap.clear();
m_idToLayer.clear();
+ m_pseudoElementToIdMap.clear();
+ m_idToPseudoElement.clear();
}
void InspectorLayerTreeAgent::enable(ErrorString*)
@@ -109,6 +112,11 @@
unbind(renderLayer);
}
+void InspectorLayerTreeAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
+{
+ unbindPseudoElement(pseudoElement);
+}
+
void InspectorLayerTreeAgent::layersForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >& layers)
{
layers = TypeBuilder::Array<TypeBuilder::LayerTree::Layer>::create();
@@ -150,12 +158,20 @@
PassRefPtr<TypeBuilder::LayerTree::Layer> InspectorLayerTreeAgent::buildObjectForLayer(ErrorString* errorString, RenderLayer* renderLayer)
{
- bool isReflection = renderLayer->isReflection();
-
RenderObject* renderer = renderLayer->renderer();
RenderLayerBacking* backing = renderLayer->backing();
- Node* node = isReflection ? renderer->parent()->node() : renderer->node();
+ Node* node = renderer->node();
+ bool isReflection = renderLayer->isReflection();
+ bool isGenerated = (isReflection ? renderer->parent() : renderer)->isBeforeOrAfterContent();
+
+ if (isReflection && isGenerated)
+ node = renderer->parent()->generatingNode();
+ else if (isGenerated)
+ node = renderer->generatingNode();
+ else if (isReflection)
+ node = renderer->parent()->node();
+
// Basic set of properties.
RefPtr<TypeBuilder::LayerTree::Layer> layerObject = TypeBuilder::LayerTree::Layer::create()
.setLayerId(bind(renderLayer))
@@ -171,6 +187,17 @@
if (isReflection)
layerObject->setIsReflection(true);
+ if (isGenerated) {
+ if (isReflection)
+ renderer = renderer->parent();
+ layerObject->setIsGeneratedContent(true);
+ layerObject->setPseudoElementId(bindPseudoElement(static_cast<PseudoElement*>(renderer->node())));
+ if (renderer->isBeforeContent())
+ layerObject->setPseudoClass("before");
+ else if (renderer->isAfterContent())
+ layerObject->setPseudoClass("after");
+ }
+
return layerObject;
}
@@ -197,7 +224,7 @@
String InspectorLayerTreeAgent::bind(const RenderLayer* layer)
{
if (!layer)
- return "";
+ return emptyString();
String identifier = m_documentLayerToIdMap.get(layer);
if (identifier.isNull()) {
identifier = IdentifiersFactory::createIdentifier();
@@ -209,14 +236,35 @@
void InspectorLayerTreeAgent::unbind(const RenderLayer* layer)
{
- String identifier = m_documentLayerToIdMap.get(layer);
- if (identifier.isNull())
+ HashMap<const RenderLayer*, String>::iterator iterator = m_documentLayerToIdMap.find(layer);
+ if (iterator == m_documentLayerToIdMap.end())
return;
+ m_idToLayer.remove(iterator->value);
+ m_documentLayerToIdMap.remove(iterator);
+}
- m_documentLayerToIdMap.remove(layer);
- m_idToLayer.remove(identifier);
+String InspectorLayerTreeAgent::bindPseudoElement(PseudoElement* pseudoElement)
+{
+ if (!pseudoElement)
+ return emptyString();
+ String identifier = m_pseudoElementToIdMap.get(pseudoElement);
+ if (identifier.isNull()) {
+ identifier = IdentifiersFactory::createIdentifier();
+ m_pseudoElementToIdMap.set(pseudoElement, identifier);
+ m_idToPseudoElement.set(identifier, pseudoElement);
+ }
+ return identifier;
}
+void InspectorLayerTreeAgent::unbindPseudoElement(PseudoElement* pseudoElement)
+{
+ HashMap<PseudoElement*, String>::iterator iterator = m_pseudoElementToIdMap.find(pseudoElement);
+ if (iterator == m_pseudoElementToIdMap.end())
+ return;
+ m_idToPseudoElement.remove(iterator->value);
+ m_pseudoElementToIdMap.remove(iterator);
+}
+
} // namespace WebCore
#endif // ENABLE(INSPECTOR)
Modified: trunk/Source/WebCore/inspector/InspectorLayerTreeAgent.h (145056 => 145057)
--- trunk/Source/WebCore/inspector/InspectorLayerTreeAgent.h 2013-03-07 10:08:03 UTC (rev 145056)
+++ trunk/Source/WebCore/inspector/InspectorLayerTreeAgent.h 2013-03-07 10:39:15 UTC (rev 145057)
@@ -62,6 +62,7 @@
void layerTreeDidChange();
void renderLayerDestroyed(const RenderLayer*);
+ void pseudoElementDestroyed(PseudoElement*);
// Called from the front-end.
virtual void enable(ErrorString*);
@@ -82,11 +83,17 @@
PassRefPtr<TypeBuilder::LayerTree::IntRect> buildObjectForIntRect(const IntRect&);
int idForNode(ErrorString*, Node*);
-
+
+ String bindPseudoElement(PseudoElement*);
+ void unbindPseudoElement(PseudoElement*);
+
InspectorFrontend::LayerTree* m_frontend;
HashMap<const RenderLayer*, String> m_documentLayerToIdMap;
HashMap<String, const RenderLayer*> m_idToLayer;
+
+ HashMap<PseudoElement*, String> m_pseudoElementToIdMap;
+ HashMap<String, PseudoElement*> m_idToPseudoElement;
};
} // namespace WebCore