1 /*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "config.h"
33
34 #include "core/inspector/InspectorLayerTreeAgent.h"
35
36 #include "core/inspector/IdentifiersFactory.h"
37 #include "core/inspector/InspectorDOMAgent.h"
38 #include "core/inspector/InspectorState.h"
39 #include "core/inspector/InstrumentingAgents.h"
40 #include "core/loader/DocumentLoader.h"
41 #include "core/frame/Frame.h"
42 #include "core/page/Page.h"
43 #include "core/rendering/CompositedLayerMapping.h"
44 #include "core/rendering/RenderLayerCompositor.h"
45 #include "core/rendering/RenderView.h"
46 #include "platform/geometry/IntRect.h"
47 #include "platform/graphics/GraphicsContextRecorder.h"
48 #include "platform/transforms/TransformationMatrix.h"
49 #include "public/platform/WebCompositingReasons.h"
50 #include "public/platform/WebLayer.h"
51
52 namespace WebCore {
53
54 unsigned InspectorLayerTreeAgent::s_lastSnapshotId;
55
56 struct LayerSnapshot {
LayerSnapshotWebCore::LayerSnapshot57 LayerSnapshot()
58 : layerId(0)
59 {
60 }
LayerSnapshotWebCore::LayerSnapshot61 LayerSnapshot(int layerId, PassRefPtr<GraphicsContextSnapshot> graphicsSnapshot)
62 : layerId(layerId)
63 , graphicsSnapshot(graphicsSnapshot)
64 {
65 }
66 int layerId;
67 RefPtr<GraphicsContextSnapshot> graphicsSnapshot;
68 };
69
idForLayer(const GraphicsLayer * graphicsLayer)70 inline String idForLayer(const GraphicsLayer* graphicsLayer)
71 {
72 return String::number(graphicsLayer->platformLayer()->id());
73 }
74
buildObjectForLayer(GraphicsLayer * graphicsLayer,int nodeId)75 static PassRefPtr<TypeBuilder::LayerTree::Layer> buildObjectForLayer(GraphicsLayer* graphicsLayer, int nodeId)
76 {
77 RefPtr<TypeBuilder::LayerTree::Layer> layerObject = TypeBuilder::LayerTree::Layer::create()
78 .setLayerId(idForLayer(graphicsLayer))
79 .setOffsetX(graphicsLayer->position().x())
80 .setOffsetY(graphicsLayer->position().y())
81 .setWidth(graphicsLayer->size().width())
82 .setHeight(graphicsLayer->size().height())
83 .setPaintCount(graphicsLayer->paintCount());
84
85 if (nodeId)
86 layerObject->setNodeId(nodeId);
87
88 GraphicsLayer* parent = graphicsLayer->parent();
89 if (!parent)
90 parent = graphicsLayer->replicatedLayer();
91 if (parent)
92 layerObject->setParentLayerId(idForLayer(parent));
93 if (!graphicsLayer->contentsAreVisible())
94 layerObject->setInvisible(true);
95 const TransformationMatrix& transform = graphicsLayer->transform();
96 if (!transform.isIdentity()) {
97 TransformationMatrix::FloatMatrix4 flattenedMatrix;
98 transform.toColumnMajorFloatArray(flattenedMatrix);
99 RefPtr<TypeBuilder::Array<double> > transformArray = TypeBuilder::Array<double>::create();
100 for (size_t i = 0; i < WTF_ARRAY_LENGTH(flattenedMatrix); ++i)
101 transformArray->addItem(flattenedMatrix[i]);
102 layerObject->setTransform(transformArray);
103 const FloatPoint3D& anchor = graphicsLayer->anchorPoint();
104 layerObject->setAnchorX(anchor.x());
105 layerObject->setAnchorY(anchor.y());
106 layerObject->setAnchorZ(anchor.z());
107 }
108 return layerObject;
109 }
110
gatherGraphicsLayers(GraphicsLayer * root,HashMap<int,int> & layerIdToNodeIdMap,RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer>> & layers)111 void gatherGraphicsLayers(GraphicsLayer* root, HashMap<int, int>& layerIdToNodeIdMap, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >& layers)
112 {
113 int layerId = root->platformLayer()->id();
114 layers->addItem(buildObjectForLayer(root, layerIdToNodeIdMap.get(layerId)));
115 if (GraphicsLayer* replica = root->replicaLayer())
116 gatherGraphicsLayers(replica, layerIdToNodeIdMap, layers);
117 for (size_t i = 0, size = root->children().size(); i < size; ++i)
118 gatherGraphicsLayers(root->children()[i], layerIdToNodeIdMap, layers);
119 }
120
InspectorLayerTreeAgent(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * state,InspectorDOMAgent * domAgent,Page * page)121 InspectorLayerTreeAgent::InspectorLayerTreeAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InspectorDOMAgent* domAgent, Page* page)
122 : InspectorBaseAgent<InspectorLayerTreeAgent>("LayerTree", instrumentingAgents, state)
123 , m_frontend(0)
124 , m_page(page)
125 , m_domAgent(domAgent)
126 {
127 }
128
~InspectorLayerTreeAgent()129 InspectorLayerTreeAgent::~InspectorLayerTreeAgent()
130 {
131 }
132
setFrontend(InspectorFrontend * frontend)133 void InspectorLayerTreeAgent::setFrontend(InspectorFrontend* frontend)
134 {
135 m_frontend = frontend->layertree();
136 }
137
clearFrontend()138 void InspectorLayerTreeAgent::clearFrontend()
139 {
140 m_frontend = 0;
141 disable(0);
142 }
143
restore()144 void InspectorLayerTreeAgent::restore()
145 {
146 // We do not re-enable layer agent automatically after navigation. This is because
147 // it depends on DOMAgent and node ids in particular, so we let front-end request document
148 // and re-enable the agent manually after this.
149 }
150
enable(ErrorString *)151 void InspectorLayerTreeAgent::enable(ErrorString*)
152 {
153 m_instrumentingAgents->setInspectorLayerTreeAgent(this);
154 layerTreeDidChange();
155 }
156
disable(ErrorString *)157 void InspectorLayerTreeAgent::disable(ErrorString*)
158 {
159 m_instrumentingAgents->setInspectorLayerTreeAgent(0);
160 m_snapshotById.clear();
161 }
162
layerTreeDidChange()163 void InspectorLayerTreeAgent::layerTreeDidChange()
164 {
165 m_frontend->layerTreeDidChange(buildLayerTree());
166 }
167
didPaint(RenderObject *,const GraphicsLayer * graphicsLayer,GraphicsContext *,const LayoutRect & rect)168 void InspectorLayerTreeAgent::didPaint(RenderObject*, const GraphicsLayer* graphicsLayer, GraphicsContext*, const LayoutRect& rect)
169 {
170 // Should only happen for FrameView paints when compositing is off. Consider different instrumentation method for that.
171 if (!graphicsLayer)
172 return;
173
174 RefPtr<TypeBuilder::DOM::Rect> domRect = TypeBuilder::DOM::Rect::create()
175 .setX(rect.x())
176 .setY(rect.y())
177 .setWidth(rect.width())
178 .setHeight(rect.height());
179 m_frontend->layerPainted(idForLayer(graphicsLayer), domRect.release());
180 }
181
buildLayerTree()182 PassRefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> > InspectorLayerTreeAgent::buildLayerTree()
183 {
184 RenderLayerCompositor* compositor = renderLayerCompositor();
185 if (!compositor || !compositor->inCompositingMode())
186 return 0;
187 LayerIdToNodeIdMap layerIdToNodeIdMap;
188 RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> > layers = TypeBuilder::Array<TypeBuilder::LayerTree::Layer>::create();
189 buildLayerIdToNodeIdMap(compositor->rootRenderLayer(), layerIdToNodeIdMap);
190 gatherGraphicsLayers(compositor->rootGraphicsLayer(), layerIdToNodeIdMap, layers);
191 return layers.release();
192 }
193
buildLayerIdToNodeIdMap(RenderLayer * root,LayerIdToNodeIdMap & layerIdToNodeIdMap)194 void InspectorLayerTreeAgent::buildLayerIdToNodeIdMap(RenderLayer* root, LayerIdToNodeIdMap& layerIdToNodeIdMap)
195 {
196 if (root->hasCompositedLayerMapping()) {
197 if (Node* node = root->renderer()->generatingNode()) {
198 GraphicsLayer* graphicsLayer = root->compositedLayerMapping()->childForSuperlayers();
199 layerIdToNodeIdMap.set(graphicsLayer->platformLayer()->id(), idForNode(node));
200 }
201 }
202 for (RenderLayer* child = root->firstChild(); child; child = child->nextSibling())
203 buildLayerIdToNodeIdMap(child, layerIdToNodeIdMap);
204 if (!root->renderer()->isRenderIFrame())
205 return;
206 FrameView* childFrameView = toFrameView(toRenderWidget(root->renderer())->widget());
207 if (RenderView* childRenderView = childFrameView->renderView()) {
208 if (RenderLayerCompositor* childCompositor = childRenderView->compositor())
209 buildLayerIdToNodeIdMap(childCompositor->rootRenderLayer(), layerIdToNodeIdMap);
210 }
211 }
212
idForNode(Node * node)213 int InspectorLayerTreeAgent::idForNode(Node* node)
214 {
215 int nodeId = m_domAgent->boundNodeId(node);
216 if (!nodeId) {
217 ErrorString ignoredError;
218 nodeId = m_domAgent->pushNodeToFrontend(&ignoredError, m_domAgent->boundNodeId(&node->document()), node);
219 }
220 return nodeId;
221 }
222
renderLayerCompositor()223 RenderLayerCompositor* InspectorLayerTreeAgent::renderLayerCompositor()
224 {
225 RenderView* renderView = m_page->mainFrame()->contentRenderer();
226 RenderLayerCompositor* compositor = renderView ? renderView->compositor() : 0;
227 return compositor;
228 }
229
findLayerById(GraphicsLayer * root,int layerId)230 static GraphicsLayer* findLayerById(GraphicsLayer* root, int layerId)
231 {
232 if (root->platformLayer()->id() == layerId)
233 return root;
234 if (root->replicaLayer()) {
235 if (GraphicsLayer* layer = findLayerById(root->replicaLayer(), layerId))
236 return layer;
237 }
238 for (size_t i = 0, size = root->children().size(); i < size; ++i) {
239 if (GraphicsLayer* layer = findLayerById(root->children()[i], layerId))
240 return layer;
241 }
242 return 0;
243 }
244
layerById(ErrorString * errorString,const String & layerId)245 GraphicsLayer* InspectorLayerTreeAgent::layerById(ErrorString* errorString, const String& layerId)
246 {
247 bool ok;
248 int id = layerId.toInt(&ok);
249 if (!ok) {
250 *errorString = "Invalid layer id";
251 return 0;
252 }
253 RenderLayerCompositor* compositor = renderLayerCompositor();
254 if (!compositor) {
255 *errorString = "Not in compositing mode";
256 return 0;
257 }
258
259 GraphicsLayer* result = findLayerById(compositor->rootGraphicsLayer(), id);
260 if (!result)
261 *errorString = "No layer matching given id found";
262 return result;
263 }
264
265 struct CompositingReasonToProtocolName {
266 uint64_t mask;
267 const char *protocolName;
268 };
269
270
compositingReasons(ErrorString * errorString,const String & layerId,RefPtr<TypeBuilder::Array<String>> & reasonStrings)271 void InspectorLayerTreeAgent::compositingReasons(ErrorString* errorString, const String& layerId, RefPtr<TypeBuilder::Array<String> >& reasonStrings)
272 {
273 static CompositingReasonToProtocolName compositingReasonNames[] = {
274 { CompositingReason3DTransform, "transform3D" },
275 { CompositingReasonVideo, "video" },
276 { CompositingReasonCanvas, "canvas" },
277 { CompositingReasonPlugin, "plugin" },
278 { CompositingReasonIFrame, "iFrame" },
279 { CompositingReasonBackfaceVisibilityHidden, "backfaceVisibilityHidden" },
280 { CompositingReasonAnimation, "animation" },
281 { CompositingReasonFilters, "filters" },
282 { CompositingReasonPositionFixed, "positionFixed" },
283 { CompositingReasonPositionSticky, "positionSticky" },
284 { CompositingReasonOverflowScrollingTouch, "overflowScrollingTouch" },
285 { CompositingReasonAssumedOverlap, "assumedOverlap" },
286 { CompositingReasonOverlap, "overlap" },
287 { CompositingReasonNegativeZIndexChildren, "negativeZIndexChildren" },
288 { CompositingReasonTransformWithCompositedDescendants, "transformWithCompositedDescendants" },
289 { CompositingReasonOpacityWithCompositedDescendants, "opacityWithCompositedDescendants" },
290 { CompositingReasonMaskWithCompositedDescendants, "maskWithCompositedDescendants" },
291 { CompositingReasonReflectionWithCompositedDescendants, "reflectionWithCompositedDescendants" },
292 { CompositingReasonFilterWithCompositedDescendants, "filterWithCompositedDescendants" },
293 { CompositingReasonBlendingWithCompositedDescendants, "blendingWithCompositedDescendants" },
294 { CompositingReasonClipsCompositingDescendants, "clipsCompositingDescendants" },
295 { CompositingReasonPerspective, "perspective" },
296 { CompositingReasonPreserve3D, "preserve3D" },
297 { CompositingReasonRoot, "root" },
298 { CompositingReasonLayerForClip, "layerForClip" },
299 { CompositingReasonLayerForScrollbar, "layerForScrollbar" },
300 { CompositingReasonLayerForScrollingContainer, "layerForScrollingContainer" },
301 { CompositingReasonLayerForForeground, "layerForForeground" },
302 { CompositingReasonLayerForBackground, "layerForBackground" },
303 { CompositingReasonLayerForMask, "layerForMask" },
304 { CompositingReasonLayerForVideoOverlay, "layerForVideoOverlay" },
305 { CompositingReasonIsolateCompositedDescendants, "isolateCompositedDescendants" }
306 };
307
308 const GraphicsLayer* graphicsLayer = layerById(errorString, layerId);
309 if (!graphicsLayer)
310 return;
311 blink::WebCompositingReasons reasonsBitmask = graphicsLayer->compositingReasons();
312 reasonStrings = TypeBuilder::Array<String>::create();
313 for (size_t i = 0; i < WTF_ARRAY_LENGTH(compositingReasonNames); ++i) {
314 if (!(reasonsBitmask & compositingReasonNames[i].mask))
315 continue;
316 reasonStrings->addItem(compositingReasonNames[i].protocolName);
317 #ifndef _NDEBUG
318 reasonsBitmask &= ~compositingReasonNames[i].mask;
319 #endif
320 }
321 ASSERT(!reasonsBitmask);
322 }
323
makeSnapshot(ErrorString * errorString,const String & layerId,String * snapshotId)324 void InspectorLayerTreeAgent::makeSnapshot(ErrorString* errorString, const String& layerId, String* snapshotId)
325 {
326 GraphicsLayer* layer = layerById(errorString, layerId);
327 if (!layer)
328 return;
329
330 GraphicsContextRecorder recorder;
331 IntSize size = expandedIntSize(layer->size());
332 GraphicsContext* context = recorder.record(size, layer->contentsOpaque());
333 layer->paint(*context, IntRect(IntPoint(0, 0), size));
334 RefPtr<GraphicsContextSnapshot> snapshot = recorder.stop();
335 *snapshotId = String::number(++s_lastSnapshotId);
336 bool newEntry = m_snapshotById.add(*snapshotId, LayerSnapshot(layer->platformLayer()->id(), snapshot)).isNewEntry;
337 ASSERT_UNUSED(newEntry, newEntry);
338 }
339
releaseSnapshot(ErrorString * errorString,const String & snapshotId)340 void InspectorLayerTreeAgent::releaseSnapshot(ErrorString* errorString, const String& snapshotId)
341 {
342 SnapshotById::iterator it = m_snapshotById.find(snapshotId);
343 if (it == m_snapshotById.end()) {
344 *errorString = "Snapshot not found";
345 return;
346 }
347 m_snapshotById.remove(it);
348 }
349
snapshotById(ErrorString * errorString,const String & snapshotId)350 const LayerSnapshot* InspectorLayerTreeAgent::snapshotById(ErrorString* errorString, const String& snapshotId)
351 {
352 SnapshotById::iterator it = m_snapshotById.find(snapshotId);
353 if (it == m_snapshotById.end()) {
354 *errorString = "Snapshot not found";
355 return 0;
356 }
357 return &it->value;
358 }
359
replaySnapshot(ErrorString * errorString,const String & snapshotId,const int * fromStep,const int * toStep,String * dataURL)360 void InspectorLayerTreeAgent::replaySnapshot(ErrorString* errorString, const String& snapshotId, const int* fromStep, const int* toStep, String* dataURL)
361 {
362 const LayerSnapshot* snapshot = snapshotById(errorString, snapshotId);
363 if (!snapshot)
364 return;
365 OwnPtr<ImageBuffer> imageBuffer = snapshot->graphicsSnapshot->replay(fromStep ? *fromStep : 0, toStep ? *toStep : 0);
366 *dataURL = imageBuffer->toDataURL("image/png");
367 }
368
profileSnapshot(ErrorString * errorString,const String & snapshotId,const int * minRepeatCount,const double * minDuration,RefPtr<TypeBuilder::Array<TypeBuilder::Array<double>>> & outTimings)369 void InspectorLayerTreeAgent::profileSnapshot(ErrorString* errorString, const String& snapshotId, const int* minRepeatCount, const double* minDuration, RefPtr<TypeBuilder::Array<TypeBuilder::Array<double> > >& outTimings)
370 {
371 const LayerSnapshot* snapshot = snapshotById(errorString, snapshotId);
372 if (!snapshot)
373 return;
374 OwnPtr<GraphicsContextSnapshot::Timings> timings = snapshot->graphicsSnapshot->profile(minRepeatCount ? *minRepeatCount : 1, minDuration ? *minDuration : 0);
375 outTimings = TypeBuilder::Array<TypeBuilder::Array<double> >::create();
376 for (size_t i = 0; i < timings->size(); ++i) {
377 const Vector<double>& row = (*timings)[i];
378 RefPtr<TypeBuilder::Array<double> > outRow = TypeBuilder::Array<double>::create();
379 for (size_t j = 1; j < row.size(); ++j)
380 outRow->addItem(row[j] - row[j - 1]);
381 outTimings->addItem(outRow.release());
382 }
383 }
384
385 } // namespace WebCore
386