Source/WebCore/ChangeLog

112013-03-04 Antoine Quint <graouts@apple.com>
22
 3 Web Inspector: allow retrieval of composited layers in a given DOM subtree
 4 https://bugs.webkit.org/show_bug.cgi?id=111312
 5
 6 New test for the LayerTree.layersForNode() method.
 7
 8 Reviewed by NOBODY (OOPS!).
 9
 10 Test: inspector-protocol/layers/layers-for-node.html
 11
 12 * inspector/Inspector.json:
 13 * inspector/InspectorLayerTreeAgent.cpp:
 14 (WebCore::InspectorLayerTreeAgent::layersForNode):
 15 (WebCore):
 16 (WebCore::InspectorLayerTreeAgent::gatherLayersUsingRenderObjectHierarchy):
 17 (WebCore::InspectorLayerTreeAgent::gatherLayersUsingRenderLayerHierarchy):
 18 (WebCore::InspectorLayerTreeAgent::buildObjectForLayer):
 19 (WebCore::InspectorLayerTreeAgent::idForNode):
 20 * inspector/InspectorLayerTreeAgent.h:
 21 (InspectorLayerTreeAgent):
 22
 232013-03-04 Antoine Quint <graouts@apple.com>
 24
325 Web Inspector: remove existing LayerTreeAgent protocol APIs
426 https://bugs.webkit.org/show_bug.cgi?id=111251
527

Source/WebCore/inspector/Inspector.json

36623662 "description": "Information about a compositing layer.",
36633663 "properties": [
36643664 { "name": "layerId", "$ref": "LayerId", "description": "The unique id for this layer." },
 3665 { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The id for the node associated with this layer." },
36653666 { "name": "bounds", "$ref": "IntRect", "description": "Bounds of the layer." },
3666  { "name": "isComposited", "type": "boolean", "optional": true, "description": "Indicates whether this layer is composited." },
3667  { "name": "paintCount", "type": "integer", "optional": true, "description": "Indicates how many time this layer has painted." },
3668  { "name": "memory", "type": "integer", "optional": true, "description": "Estimated memory used by this layer." },
3669  { "name": "compositedBounds", "$ref": "IntRect", "optional": true, "description": "The bounds of the composited layer." }
 3667 { "name": "paintCount", "type": "integer", "description": "Indicates how many time this layer has painted." },
 3668 { "name": "memory", "type": "integer", "description": "Estimated memory used by this layer." },
 3669 { "name": "compositedBounds", "$ref": "IntRect", "description": "The bounds of the composited layer." },
 3670 { "name": "isInShadowTree", "type": "boolean", "optional": true, "description": "Indicates whether this layer is associated with an element hosted in a shadow tree." }
36703671 ]
36713672 }
36723673 ],

36783679 {
36793680 "name": "disable",
36803681 "description": "Disables compositing tree inspection."
 3682 },
 3683 {
 3684 "name": "layersForNode",
 3685 "parameters": [
 3686 { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Root of the subtree for which we want to gather layers." } ],
 3687 "description": "Returns the layer tree structure of the current page.",
 3688 "returns": [
 3689 { "name": "layers", "type": "array", "items": { "$ref": "Layer" }, "description": "Child layers." }
 3690 ]
36813691 }
36823692 ],
36833693 "events": [

Source/WebCore/inspector/InspectorLayerTreeAgent.cpp

@@void InspectorLayerTreeAgent::renderLayerDestroyed(const RenderLayer* renderLaye
111111 unbind(renderLayer);
112112}
113113
114 PassRefPtr<TypeBuilder::LayerTree::Layer> InspectorLayerTreeAgent::buildObjectForLayer(RenderLayer* renderLayer)
 114void InspectorLayerTreeAgent::layersForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >& layers)
115115{
 116 layers = TypeBuilder::Array<TypeBuilder::LayerTree::Layer>::create();
 117
 118 Node* node = m_instrumentingAgents->inspectorDOMAgent()->nodeForId(nodeId);
 119 if (!node) {
 120 *errorString = "Provided node id doesn't match any known node";
 121 return;
 122 }
 123
 124 RenderObject* renderer = node->renderer();
 125 if (!renderer) {
 126 *errorString = "Node for provided node id doesn't have a renderer";
 127 return;
 128 }
 129
 130 gatherLayersUsingRenderObjectHierarchy(errorString, renderer, layers);
 131}
 132
 133void InspectorLayerTreeAgent::gatherLayersUsingRenderObjectHierarchy(ErrorString* errorString, RenderObject* renderer, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >& layers)
 134{
 135 if (renderer->hasLayer()) {
 136 gatherLayersUsingRenderLayerHierarchy(errorString, renderer->enclosingLayer(), layers);
 137 return;
 138 }
 139
 140 for (renderer = renderer->firstChild(); renderer; renderer = renderer->nextSibling())
 141 gatherLayersUsingRenderObjectHierarchy(errorString, renderer, layers);
 142}
 143
 144void InspectorLayerTreeAgent::gatherLayersUsingRenderLayerHierarchy(ErrorString* errorString, RenderLayer* renderLayer, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >& layers)
 145{
 146 if (renderLayer->isComposited())
 147 layers->addItem(buildObjectForLayer(errorString, renderLayer));
 148
 149 for (renderLayer = renderLayer->firstChild(); renderLayer; renderLayer = renderLayer->nextSibling())
 150 gatherLayersUsingRenderLayerHierarchy(errorString, renderLayer, layers);
 151}
 152
 153PassRefPtr<TypeBuilder::LayerTree::Layer> InspectorLayerTreeAgent::buildObjectForLayer(ErrorString* errorString, RenderLayer* renderLayer)
 154{
 155 Node* node = renderLayer->renderer()->node();
 156 RenderLayerBacking* backing = renderLayer->backing();
 157
116158 // Basic set of properties.
117159 RefPtr<TypeBuilder::LayerTree::Layer> layerObject = TypeBuilder::LayerTree::Layer::create()
118160 .setLayerId(bind(renderLayer))
119  .setBounds(buildObjectForIntRect(enclosingIntRect(renderLayer->localBoundingBox())));
120 
121  // Optional properties for composited layers only.
122  if (renderLayer->isComposited()) {
123  RenderLayerBacking* backing = renderLayer->backing();
124  layerObject->setMemory(backing->backingStoreMemoryEstimate());
125  layerObject->setCompositedBounds(buildObjectForIntRect(backing->compositedBounds()));
126  layerObject->setPaintCount(backing->graphicsLayer()->repaintCount());
127  layerObject->setIsComposited(true);
128  }
 161 .setNodeId(idForNode(errorString, node))
 162 .setBounds(buildObjectForIntRect(enclosingIntRect(renderLayer->localBoundingBox())))
 163 .setMemory(backing->backingStoreMemoryEstimate())
 164 .setCompositedBounds(buildObjectForIntRect(backing->compositedBounds()))
 165 .setPaintCount(backing->graphicsLayer()->repaintCount());
 166
 167 if (node && node->shadowHost())
 168 layerObject->setIsInShadowTree(true);
129169
130170 return layerObject;
131171}
132172
 173int InspectorLayerTreeAgent::idForNode(ErrorString* errorString, Node* node)
 174{
 175 InspectorDOMAgent* domAgent = m_instrumentingAgents->inspectorDOMAgent();
 176
 177 int id = domAgent->boundNodeId(node);
 178 if (!id)
 179 id = domAgent->pushNodeToFrontend(errorString, domAgent->boundNodeId(node->document()), node);
 180
 181 return id;
 182}
 183
133184PassRefPtr<TypeBuilder::LayerTree::IntRect> InspectorLayerTreeAgent::buildObjectForIntRect(const IntRect& rect)
134185{
135186 return TypeBuilder::LayerTree::IntRect::create()

Source/WebCore/inspector/InspectorLayerTreeAgent.h

@@public:
6767 // Called from the front-end.
6868 virtual void enable(ErrorString*);
6969 virtual void disable(ErrorString*);
 70 virtual void layersForNode(ErrorString*, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >&);
7071
7172private:
7273 InspectorLayerTreeAgent(InstrumentingAgents*, InspectorCompositeState*, Page*);

@@private:
7576 String bind(const RenderLayer*);
7677 void unbind(const RenderLayer*);
7778
78  PassRefPtr<TypeBuilder::LayerTree::Layer> buildObjectForLayer(RenderLayer*);
 79 void gatherLayersUsingRenderObjectHierarchy(ErrorString*, RenderObject*, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >&);
 80 void gatherLayersUsingRenderLayerHierarchy(ErrorString*, RenderLayer*, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >&);
 81
 82 PassRefPtr<TypeBuilder::LayerTree::Layer> buildObjectForLayer(ErrorString*, RenderLayer*);
7983 PassRefPtr<TypeBuilder::LayerTree::IntRect> buildObjectForIntRect(const IntRect&);
 84
 85 int idForNode(ErrorString*, Node*);
8086
8187 Page* m_inspectedPage;
8288 InspectorFrontend::LayerTree* m_frontend;

LayoutTests/ChangeLog

112013-03-04 Antoine Quint <graouts@apple.com>
22
 3 Web Inspector: allow retrieval of composited layers in a given DOM subtree
 4 https://bugs.webkit.org/show_bug.cgi?id=111312
 5
 6 Introduce the LayerTreeAgent.layersForNode(node) method allowing the front-end
 7 to gather all composited layers associated with nodes in the subtree of which
 8 the provided node is the root.
 9
 10 In order to gather the layers in the subtree, we first traverse the node's
 11 renderer's RenderObject hierarchy and whenever we encounter a RenderObject
 12 that has a RenderLayer, we then traverse that renderLayer's RenderLayer
 13 hierarchy. This allows for a quick path through the relevant objects we're
 14 gathering.
 15
 16 Layers gathered will push the node to which they're associated, allowing a
 17 nodeId for this layer to be listed in the Layer object sent to the front-end.
 18 It is crucial to be able to provide a nodeId as well as a layerId for a Layer
 19 object in order to be able to correctly assess mutations in the layer tree.
 20 For instance, it is expected that a node's layer be replaced by a new layer
 21 to represent a slightly different rendering of its content, but the front-end
 22 should be able to represent this layer as an object for which only certain
 23 attributes have changed (like the "paintCount" property).
 24
 25 Layer objects also indicate whether they're associated to a node hosted in a
 26 shadow tree (the optional "isInShadowTree" property) in order for the front-end
 27 to be able to only show this layer if the option to show nodes hosted in shadow
 28 tree is enabled.
 29
 30 Finally, since we're only gathering composited layers, we're removing the
 31 "isLayerComposited" property and removing the optional flag on the "paintCount",
 32 "memory" and "compositedBounds" properties.
 33
 34 Reviewed by NOBODY (OOPS!).
 35
 36 * inspector-protocol/layers/layers-for-node-expected.txt: Added.
 37 * inspector-protocol/layers/layers-for-node.html: Added.
 38
 392013-03-04 Antoine Quint <graouts@apple.com>
 40
341 Web Inspector: remove existing LayerTreeAgent protocol APIs
442 https://bugs.webkit.org/show_bug.cgi?id=111251
543

LayoutTests/inspector-protocol/layers/layers-for-node-expected.txt

 1
 2=== Enable the LayerTree agent ===
 3
 4PASS
 5
 6=== Get the Document ===
 7
 8PASS
 9
 10=== Get the initial layer tree ===
 11
 12PASS
 13
 14[
 15 {
 16 "layerId": "string",
 17 "nodeId": "number",
 18 "bounds": {
 19 "x": 0,
 20 "y": 0,
 21 "width": "number",
 22 "height": "number"
 23 },
 24 "memory": "number",
 25 "compositedBounds": {
 26 "x": 0,
 27 "y": 0,
 28 "width": "number",
 29 "height": "number"
 30 },
 31 "paintCount": "number"
 32 },
 33 {
 34 "layerId": "string",
 35 "nodeId": "number",
 36 "bounds": {
 37 "x": 0,
 38 "y": 0,
 39 "width": 50,
 40 "height": 50
 41 },
 42 "memory": "number",
 43 "compositedBounds": {
 44 "x": 0,
 45 "y": 0,
 46 "width": 50,
 47 "height": 50
 48 },
 49 "paintCount": "number"
 50 },
 51 {
 52 "layerId": "string",
 53 "nodeId": "number",
 54 "bounds": {
 55 "x": 0,
 56 "y": 0,
 57 "width": 50,
 58 "height": 50
 59 },
 60 "memory": "number",
 61 "compositedBounds": {
 62 "x": 0,
 63 "y": 0,
 64 "width": 50,
 65 "height": 50
 66 },
 67 "paintCount": "number"
 68 },
 69 {
 70 "layerId": "string",
 71 "nodeId": "number",
 72 "bounds": {
 73 "x": 0,
 74 "y": 0,
 75 "width": 100,
 76 "height": 100
 77 },
 78 "memory": "number",
 79 "compositedBounds": {
 80 "x": 0,
 81 "y": 0,
 82 "width": 100,
 83 "height": 100
 84 },
 85 "paintCount": "number"
 86 },
 87 {
 88 "layerId": "string",
 89 "nodeId": "number",
 90 "bounds": {
 91 "x": 0,
 92 "y": 0,
 93 "width": 50,
 94 "height": 50
 95 },
 96 "memory": "number",
 97 "compositedBounds": {
 98 "x": 0,
 99 "y": 0,
 100 "width": 50,
 101 "height": 50
 102 },
 103 "paintCount": "number"
 104 },
 105 {
 106 "layerId": "string",
 107 "nodeId": "number",
 108 "bounds": {
 109 "x": 0,
 110 "y": 0,
 111 "width": "number",
 112 "height": "number"
 113 },
 114 "memory": "number",
 115 "compositedBounds": {
 116 "x": 0,
 117 "y": 0,
 118 "width": "number",
 119 "height": "number"
 120 },
 121 "paintCount": "number"
 122 }
 123]
 124
 125=== Message the page to add a new composited layer ===
 126
 127PASS
 128
 129=== Get the modified layer tree ===
 130
 131PASS
 132
 133[
 134 {
 135 "layerId": "string",
 136 "nodeId": "number",
 137 "bounds": {
 138 "x": 0,
 139 "y": 0,
 140 "width": "number",
 141 "height": "number"
 142 },
 143 "memory": "number",
 144 "compositedBounds": {
 145 "x": 0,
 146 "y": 0,
 147 "width": "number",
 148 "height": "number"
 149 },
 150 "paintCount": "number"
 151 },
 152 {
 153 "layerId": "string",
 154 "nodeId": "number",
 155 "bounds": {
 156 "x": 0,
 157 "y": 0,
 158 "width": 50,
 159 "height": 50
 160 },
 161 "memory": "number",
 162 "compositedBounds": {
 163 "x": 0,
 164 "y": 0,
 165 "width": 50,
 166 "height": 50
 167 },
 168 "paintCount": "number"
 169 },
 170 {
 171 "layerId": "string",
 172 "nodeId": "number",
 173 "bounds": {
 174 "x": 0,
 175 "y": 0,
 176 "width": 50,
 177 "height": 50
 178 },
 179 "memory": "number",
 180 "compositedBounds": {
 181 "x": 0,
 182 "y": 0,
 183 "width": 50,
 184 "height": 50
 185 },
 186 "paintCount": "number"
 187 },
 188 {
 189 "layerId": "string",
 190 "nodeId": "number",
 191 "bounds": {
 192 "x": 0,
 193 "y": 0,
 194 "width": 100,
 195 "height": 100
 196 },
 197 "memory": "number",
 198 "compositedBounds": {
 199 "x": 0,
 200 "y": 0,
 201 "width": 100,
 202 "height": 100
 203 },
 204 "paintCount": "number"
 205 },
 206 {
 207 "layerId": "string",
 208 "nodeId": "number",
 209 "bounds": {
 210 "x": 0,
 211 "y": 0,
 212 "width": 50,
 213 "height": 50
 214 },
 215 "memory": "number",
 216 "compositedBounds": {
 217 "x": 0,
 218 "y": 0,
 219 "width": 50,
 220 "height": 50
 221 },
 222 "paintCount": "number"
 223 },
 224 {
 225 "layerId": "string",
 226 "nodeId": "number",
 227 "bounds": {
 228 "x": 0,
 229 "y": 0,
 230 "width": "number",
 231 "height": "number"
 232 },
 233 "memory": "number",
 234 "compositedBounds": {
 235 "x": 0,
 236 "y": 0,
 237 "width": "number",
 238 "height": "number"
 239 },
 240 "paintCount": "number"
 241 },
 242 {
 243 "layerId": "string",
 244 "nodeId": "number",
 245 "bounds": {
 246 "x": 0,
 247 "y": 0,
 248 "width": 50,
 249 "height": 50
 250 },
 251 "memory": "number",
 252 "compositedBounds": {
 253 "x": 0,
 254 "y": 0,
 255 "width": 50,
 256 "height": 50
 257 },
 258 "paintCount": "number"
 259 }
 260]
 261
 262=== Get attributes for the newly inserted node ===
 263
 264PASS
 265
 266=== Test complete, all expected conditions met ===
 267

LayoutTests/inspector-protocol/layers/layers-for-node.html

 1<html>
 2<head>
 3<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/protocol-test.js"></script>
 4<script type="text/javascript">
 5
 6function addCompositedLayer()
 7{
 8 var element = document.createElement("div");
 9 element.className = "composited";
 10 element.id = "last-element";
 11 document.body.appendChild(element);
 12};
 13
 14function test()
 15{
 16 var documentNode;
 17 var initialLayers;
 18 var eventsCount = 0;
 19
 20 InspectorTest.eventHandler["LayerTree.layerTreeDidChange"] = function (messageObject) {
 21 eventsCount++;
 22 };
 23
 24 enableLayerTreeAgent();
 25
 26 function enableLayerTreeAgent(result)
 27 {
 28 step({
 29 name: "Enable the LayerTree agent",
 30 command: "LayerTree.enable",
 31 parameters: {},
 32 callback: getDocument
 33 });
 34 };
 35
 36 function getDocument(result)
 37 {
 38 step({
 39 name: "Get the Document",
 40 command: "DOM.getDocument",
 41 parameters: {},
 42 callback: getInitialLayerTree
 43 });
 44 };
 45
 46 function getInitialLayerTree(result)
 47 {
 48 documentNode = result.root;
 49 step({
 50 name: "Get the initial layer tree",
 51 command: "LayerTree.layersForNode",
 52 parameters: {"nodeId": documentNode.nodeId},
 53 callback: gotInitialLayerTree
 54 });
 55 };
 56
 57 function gotInitialLayerTree(result)
 58 {
 59 initialLayers = result.layers;
 60
 61 dumpLayers(initialLayers);
 62
 63 step({
 64 name: "Message the page to add a new composited layer",
 65 command: "Runtime.evaluate",
 66 parameters: {"expression": "addCompositedLayer()"},
 67 callback: getModifiedLayerTree
 68 });
 69 };
 70
 71 function getModifiedLayerTree(result)
 72 {
 73 step({
 74 name: "Get the modified layer tree",
 75 command: "LayerTree.layersForNode",
 76 parameters: {"nodeId": documentNode.nodeId},
 77 callback: gotModifiedLayerTree
 78 });
 79 };
 80
 81 var layerCount = 0;
 82
 83 function gotModifiedLayerTree(result)
 84 {
 85 dumpLayers(result.layers);
 86
 87 var mutations = layerMutations(initialLayers, result.layers);
 88 var newLayer = mutations.additions[0];
 89
 90 step({
 91 name: "Get attributes for the newly inserted node",
 92 command: "DOM.getAttributes",
 93 parameters: {"nodeId": newLayer.nodeId},
 94 callback: gotNodeAttributes
 95 });
 96 };
 97
 98 function gotNodeAttributes(result)
 99 {
 100 var attributes = attributesDictionaryFromArray(result.attributes);
 101 if (attributes.id !== "last-element")
 102 InspectorTest.log("FAIL: Did not obtain the expected element for the last inserted layer.");
 103
 104 finishTest();
 105 };
 106
 107 function finishTest()
 108 {
 109 if (!eventsCount)
 110 InspectorTest.log("FAIL: Did not receive layerTreeDidChange events.");
 111 else
 112 InspectorTest.log("\n=== Test complete, all expected conditions met ===");
 113
 114 InspectorTest.completeTest();
 115 };
 116
 117 function layerMutations(oldLayers, newLayers)
 118 {
 119 function layerIdMap(layer) {
 120 return layer.layerId;
 121 }
 122
 123 var oldLayerIds = oldLayers.map(layerIdMap);
 124 var newLayerIds = newLayers.map(layerIdMap);
 125
 126 return {
 127 additions: newLayers.filter(function (layer) {
 128 return (oldLayerIds.indexOf(layer.layerId) === -1);
 129 }),
 130 removals: oldLayers.filter(function (layer) {
 131 return (newLayerIds.indexOf(layer.layerId) === -1);
 132 })
 133 };
 134 };
 135
 136 function attributesDictionaryFromArray(attributes)
 137 {
 138 var dictionary = {}
 139 for (var i = 0, count = attributes.length; i < count; i += 2) {
 140 dictionary[attributes[i]] = attributes[i + 1];
 141 }
 142 return dictionary;
 143 };
 144
 145 function dumpLayers(layers)
 146 {
 147 function replacer(key, value)
 148 {
 149 if (key === "layerId" || key === "nodeId" || key === "memory" || key === "paintCount")
 150 return typeof(value);
 151
 152 // some values differ based on port, but the ones we most
 153 // care about will always be less or equal 100.
 154 if ((key === "width" || key === "height") && value > 100)
 155 return typeof(value);
 156
 157 return value;
 158 };
 159
 160 InspectorTest.log("\n" + JSON.stringify(layers, replacer, " "));
 161 };
 162
 163 function step(test)
 164 {
 165 InspectorTest.log("\n=== " + test.name + " ===\n")
 166 InspectorTest.sendCommand(test.command, test.parameters, function(messageObject) {
 167 if (messageObject.hasOwnProperty("error")) {
 168 InspectorTest.log("FAIL: " + messageObject.error.message + " (" + messageObject.error.code + ")");
 169 InspectorTest.completeTest();
 170 return;
 171 }
 172
 173 InspectorTest.log("PASS");
 174 test.callback(messageObject.result);
 175 });
 176 };
 177
 178 function assert(name, actual, expected)
 179 {
 180 if (expected === actual)
 181 InspectorTest.log("PASS: " + name + ".");
 182 else
 183 InspectorTest.log("FAIL: " + name + ". Expected " + expected + " but got " + actual);
 184 };
 185
 186};
 187
 188window.addEventListener("DOMContentLoaded", function () {
 189 runTest();
 190}, false);
 191
 192</script>
 193<style type="text/css">
 194
 195 div {
 196 position: absolute;
 197 top: 0;
 198 left: 0;
 199 }
 200
 201 .regular {
 202 width: 100px;
 203 height: 100px;
 204 background-color: black;
 205 }
 206
 207 .composited {
 208 top: 25px;
 209 left: 25px;
 210 width: 50px;
 211 height: 50px;
 212 background-color: blue;
 213 -webkit-transform: translateZ(0);
 214 }
 215
 216 .offset {
 217 left: 200px;
 218 -webkit-transform: translateZ(0);
 219 }
 220
 221</style>
 222</head>
 223<body>
 224
 225 <div class="regular"></div>
 226
 227 <div class="composited">
 228 <div class="composited"></div>
 229 </div>
 230
 231 <div class="regular offset">
 232 <div class="composited"></div>
 233 </div>
 234
 235</body>
 236</html>