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/dom/Document.h"
37 #include "core/frame/LocalFrame.h"
38 #include "core/inspector/IdentifiersFactory.h"
39 #include "core/inspector/InspectorNodeIds.h"
40 #include "core/inspector/InspectorState.h"
41 #include "core/inspector/InstrumentingAgents.h"
42 #include "core/loader/DocumentLoader.h"
43 #include "core/page/Page.h"
44 #include "core/rendering/RenderView.h"
45 #include "core/rendering/RenderWidget.h"
46 #include "core/rendering/compositing/CompositedLayerMapping.h"
47 #include "core/rendering/compositing/RenderLayerCompositor.h"
48 #include "platform/geometry/IntRect.h"
49 #include "platform/graphics/CompositingReasons.h"
50 #include "platform/graphics/GraphicsContextRecorder.h"
51 #include "platform/image-encoders/skia/PNGImageEncoder.h"
52 #include "platform/transforms/TransformationMatrix.h"
53 #include "public/platform/WebFloatPoint.h"
54 #include "public/platform/WebLayer.h"
55 #include "wtf/text/Base64.h"
56 #include "wtf/text/StringBuilder.h"
57
58 namespace blink {
59
60 unsigned InspectorLayerTreeAgent::s_lastSnapshotId;
61
idForLayer(const GraphicsLayer * graphicsLayer)62 inline String idForLayer(const GraphicsLayer* graphicsLayer)
63 {
64 return String::number(graphicsLayer->platformLayer()->id());
65 }
66
buildScrollRect(const blink::WebRect & rect,const TypeBuilder::LayerTree::ScrollRect::Type::Enum & type)67 static PassRefPtr<TypeBuilder::LayerTree::ScrollRect> buildScrollRect(const blink::WebRect& rect, const TypeBuilder::LayerTree::ScrollRect::Type::Enum& type)
68 {
69 RefPtr<TypeBuilder::DOM::Rect> rectObject = TypeBuilder::DOM::Rect::create()
70 .setX(rect.x)
71 .setY(rect.y)
72 .setHeight(rect.height)
73 .setWidth(rect.width);
74 RefPtr<TypeBuilder::LayerTree::ScrollRect> scrollRectObject = TypeBuilder::LayerTree::ScrollRect::create()
75 .setRect(rectObject.release())
76 .setType(type);
77 return scrollRectObject.release();
78 }
79
buildScrollRectsForLayer(GraphicsLayer * graphicsLayer)80 static PassRefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::ScrollRect> > buildScrollRectsForLayer(GraphicsLayer* graphicsLayer)
81 {
82 RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::ScrollRect> > scrollRects = TypeBuilder::Array<TypeBuilder::LayerTree::ScrollRect>::create();
83 blink::WebLayer* webLayer = graphicsLayer->platformLayer();
84 for (size_t i = 0; i < webLayer->nonFastScrollableRegion().size(); ++i) {
85 scrollRects->addItem(buildScrollRect(webLayer->nonFastScrollableRegion()[i], TypeBuilder::LayerTree::ScrollRect::Type::RepaintsOnScroll));
86 }
87 for (size_t i = 0; i < webLayer->touchEventHandlerRegion().size(); ++i) {
88 scrollRects->addItem(buildScrollRect(webLayer->touchEventHandlerRegion()[i], TypeBuilder::LayerTree::ScrollRect::Type::TouchEventHandler));
89 }
90 if (webLayer->haveWheelEventHandlers()) {
91 blink::WebRect webRect(webLayer->position().x, webLayer->position().y, webLayer->bounds().width, webLayer->bounds().height);
92 scrollRects->addItem(buildScrollRect(webRect, TypeBuilder::LayerTree::ScrollRect::Type::WheelEventHandler));
93 }
94 return scrollRects->length() ? scrollRects.release() : nullptr;
95 }
96
buildObjectForLayer(GraphicsLayer * graphicsLayer,int nodeId)97 static PassRefPtr<TypeBuilder::LayerTree::Layer> buildObjectForLayer(GraphicsLayer* graphicsLayer, int nodeId)
98 {
99 blink::WebLayer* webLayer = graphicsLayer->platformLayer();
100 RefPtr<TypeBuilder::LayerTree::Layer> layerObject = TypeBuilder::LayerTree::Layer::create()
101 .setLayerId(idForLayer(graphicsLayer))
102 .setOffsetX(webLayer->position().x)
103 .setOffsetY(webLayer->position().y)
104 .setWidth(webLayer->bounds().width)
105 .setHeight(webLayer->bounds().height)
106 .setPaintCount(graphicsLayer->paintCount());
107
108 if (nodeId)
109 layerObject->setBackendNodeId(nodeId);
110
111 GraphicsLayer* parent = graphicsLayer->parent();
112 if (!parent)
113 parent = graphicsLayer->replicatedLayer();
114 if (parent)
115 layerObject->setParentLayerId(idForLayer(parent));
116 if (!graphicsLayer->contentsAreVisible())
117 layerObject->setInvisible(true);
118 const TransformationMatrix& transform = graphicsLayer->transform();
119 if (!transform.isIdentity()) {
120 TransformationMatrix::FloatMatrix4 flattenedMatrix;
121 transform.toColumnMajorFloatArray(flattenedMatrix);
122 RefPtr<TypeBuilder::Array<double> > transformArray = TypeBuilder::Array<double>::create();
123 for (size_t i = 0; i < WTF_ARRAY_LENGTH(flattenedMatrix); ++i)
124 transformArray->addItem(flattenedMatrix[i]);
125 layerObject->setTransform(transformArray);
126 const FloatPoint3D& transformOrigin = graphicsLayer->transformOrigin();
127 // FIXME: rename these to setTransformOrigin*
128 if (webLayer->bounds().width > 0)
129 layerObject->setAnchorX(transformOrigin.x() / webLayer->bounds().width);
130 else
131 layerObject->setAnchorX(0.0);
132 if (webLayer->bounds().height > 0)
133 layerObject->setAnchorY(transformOrigin.y() / webLayer->bounds().height);
134 else
135 layerObject->setAnchorY(0.0);
136 layerObject->setAnchorZ(transformOrigin.z());
137 }
138 RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::ScrollRect> > scrollRects = buildScrollRectsForLayer(graphicsLayer);
139 if (scrollRects)
140 layerObject->setScrollRects(scrollRects.release());
141 return layerObject;
142 }
143
InspectorLayerTreeAgent(Page * page)144 InspectorLayerTreeAgent::InspectorLayerTreeAgent(Page* page)
145 : InspectorBaseAgent<InspectorLayerTreeAgent>("LayerTree")
146 , m_frontend(0)
147 , m_page(page)
148 {
149 }
150
~InspectorLayerTreeAgent()151 InspectorLayerTreeAgent::~InspectorLayerTreeAgent()
152 {
153 }
154
trace(Visitor * visitor)155 void InspectorLayerTreeAgent::trace(Visitor* visitor)
156 {
157 visitor->trace(m_page);
158 InspectorBaseAgent::trace(visitor);
159 }
160
setFrontend(InspectorFrontend * frontend)161 void InspectorLayerTreeAgent::setFrontend(InspectorFrontend* frontend)
162 {
163 m_frontend = frontend->layertree();
164 }
165
clearFrontend()166 void InspectorLayerTreeAgent::clearFrontend()
167 {
168 m_frontend = 0;
169 disable(0);
170 }
171
restore()172 void InspectorLayerTreeAgent::restore()
173 {
174 // We do not re-enable layer agent automatically after navigation. This is because
175 // it depends on DOMAgent and node ids in particular, so we let front-end request document
176 // and re-enable the agent manually after this.
177 }
178
enable(ErrorString *)179 void InspectorLayerTreeAgent::enable(ErrorString*)
180 {
181 m_instrumentingAgents->setInspectorLayerTreeAgent(this);
182 if (LocalFrame* frame = m_page->deprecatedLocalMainFrame()) {
183 Document* document = frame->document();
184 if (document && document->lifecycle().state() >= DocumentLifecycle::CompositingClean)
185 layerTreeDidChange();
186 }
187 }
188
disable(ErrorString *)189 void InspectorLayerTreeAgent::disable(ErrorString*)
190 {
191 m_instrumentingAgents->setInspectorLayerTreeAgent(0);
192 m_snapshotById.clear();
193 ErrorString unused;
194 }
195
layerTreeDidChange()196 void InspectorLayerTreeAgent::layerTreeDidChange()
197 {
198 m_frontend->layerTreeDidChange(buildLayerTree());
199 }
200
didPaint(RenderObject *,const GraphicsLayer * graphicsLayer,GraphicsContext *,const LayoutRect & rect)201 void InspectorLayerTreeAgent::didPaint(RenderObject*, const GraphicsLayer* graphicsLayer, GraphicsContext*, const LayoutRect& rect)
202 {
203 // Should only happen for FrameView paints when compositing is off. Consider different instrumentation method for that.
204 if (!graphicsLayer)
205 return;
206
207 RefPtr<TypeBuilder::DOM::Rect> domRect = TypeBuilder::DOM::Rect::create()
208 .setX(rect.x())
209 .setY(rect.y())
210 .setWidth(rect.width())
211 .setHeight(rect.height());
212 m_frontend->layerPainted(idForLayer(graphicsLayer), domRect.release());
213 }
214
buildLayerTree()215 PassRefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> > InspectorLayerTreeAgent::buildLayerTree()
216 {
217 RenderLayerCompositor* compositor = renderLayerCompositor();
218 if (!compositor || !compositor->inCompositingMode())
219 return nullptr;
220
221 LayerIdToNodeIdMap layerIdToNodeIdMap;
222 RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> > layers = TypeBuilder::Array<TypeBuilder::LayerTree::Layer>::create();
223 buildLayerIdToNodeIdMap(compositor->rootRenderLayer(), layerIdToNodeIdMap);
224 gatherGraphicsLayers(compositor->rootGraphicsLayer(), layerIdToNodeIdMap, layers);
225 return layers.release();
226 }
227
buildLayerIdToNodeIdMap(RenderLayer * root,LayerIdToNodeIdMap & layerIdToNodeIdMap)228 void InspectorLayerTreeAgent::buildLayerIdToNodeIdMap(RenderLayer* root, LayerIdToNodeIdMap& layerIdToNodeIdMap)
229 {
230 if (root->hasCompositedLayerMapping()) {
231 if (Node* node = root->renderer()->generatingNode()) {
232 GraphicsLayer* graphicsLayer = root->compositedLayerMapping()->childForSuperlayers();
233 layerIdToNodeIdMap.set(graphicsLayer->platformLayer()->id(), idForNode(node));
234 }
235 }
236 for (RenderLayer* child = root->firstChild(); child; child = child->nextSibling())
237 buildLayerIdToNodeIdMap(child, layerIdToNodeIdMap);
238 if (!root->renderer()->isRenderIFrame())
239 return;
240 FrameView* childFrameView = toFrameView(toRenderWidget(root->renderer())->widget());
241 if (RenderView* childRenderView = childFrameView->renderView()) {
242 if (RenderLayerCompositor* childCompositor = childRenderView->compositor())
243 buildLayerIdToNodeIdMap(childCompositor->rootRenderLayer(), layerIdToNodeIdMap);
244 }
245 }
246
gatherGraphicsLayers(GraphicsLayer * root,HashMap<int,int> & layerIdToNodeIdMap,RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer>> & layers)247 void InspectorLayerTreeAgent::gatherGraphicsLayers(GraphicsLayer* root, HashMap<int, int>& layerIdToNodeIdMap, RefPtr<TypeBuilder::Array<TypeBuilder::LayerTree::Layer> >& layers)
248 {
249 int layerId = root->platformLayer()->id();
250 if (m_pageOverlayLayerIds.find(layerId) != WTF::kNotFound)
251 return;
252 layers->addItem(buildObjectForLayer(root, layerIdToNodeIdMap.get(layerId)));
253 if (GraphicsLayer* replica = root->replicaLayer())
254 gatherGraphicsLayers(replica, layerIdToNodeIdMap, layers);
255 for (size_t i = 0, size = root->children().size(); i < size; ++i)
256 gatherGraphicsLayers(root->children()[i], layerIdToNodeIdMap, layers);
257 }
258
idForNode(Node * node)259 int InspectorLayerTreeAgent::idForNode(Node* node)
260 {
261 return InspectorNodeIds::idForNode(node);
262 }
263
renderLayerCompositor()264 RenderLayerCompositor* InspectorLayerTreeAgent::renderLayerCompositor()
265 {
266 RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer();
267 RenderLayerCompositor* compositor = renderView ? renderView->compositor() : 0;
268 return compositor;
269 }
270
findLayerById(GraphicsLayer * root,int layerId)271 static GraphicsLayer* findLayerById(GraphicsLayer* root, int layerId)
272 {
273 if (root->platformLayer()->id() == layerId)
274 return root;
275 if (root->replicaLayer()) {
276 if (GraphicsLayer* layer = findLayerById(root->replicaLayer(), layerId))
277 return layer;
278 }
279 for (size_t i = 0, size = root->children().size(); i < size; ++i) {
280 if (GraphicsLayer* layer = findLayerById(root->children()[i], layerId))
281 return layer;
282 }
283 return 0;
284 }
285
layerById(ErrorString * errorString,const String & layerId)286 GraphicsLayer* InspectorLayerTreeAgent::layerById(ErrorString* errorString, const String& layerId)
287 {
288 bool ok;
289 int id = layerId.toInt(&ok);
290 if (!ok) {
291 *errorString = "Invalid layer id";
292 return 0;
293 }
294 RenderLayerCompositor* compositor = renderLayerCompositor();
295 if (!compositor) {
296 *errorString = "Not in compositing mode";
297 return 0;
298 }
299
300 GraphicsLayer* result = findLayerById(compositor->rootGraphicsLayer(), id);
301 if (!result)
302 *errorString = "No layer matching given id found";
303 return result;
304 }
305
compositingReasons(ErrorString * errorString,const String & layerId,RefPtr<TypeBuilder::Array<String>> & reasonStrings)306 void InspectorLayerTreeAgent::compositingReasons(ErrorString* errorString, const String& layerId, RefPtr<TypeBuilder::Array<String> >& reasonStrings)
307 {
308 const GraphicsLayer* graphicsLayer = layerById(errorString, layerId);
309 if (!graphicsLayer)
310 return;
311 CompositingReasons reasonsBitmask = graphicsLayer->compositingReasons();
312 reasonStrings = TypeBuilder::Array<String>::create();
313 for (size_t i = 0; i < kNumberOfCompositingReasons; ++i) {
314 if (!(reasonsBitmask & kCompositingReasonStringMap[i].reason))
315 continue;
316 reasonStrings->addItem(kCompositingReasonStringMap[i].shortName);
317 #ifndef _NDEBUG
318 reasonsBitmask &= ~kCompositingReasonStringMap[i].reason;
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, snapshot).isNewEntry;
337 ASSERT_UNUSED(newEntry, newEntry);
338 }
339
loadSnapshot(ErrorString * errorString,const String & data,String * snapshotId)340 void InspectorLayerTreeAgent::loadSnapshot(ErrorString* errorString, const String& data, String* snapshotId)
341 {
342 Vector<char> snapshotData;
343 if (!base64Decode(data, snapshotData)) {
344 *errorString = "Invalid base64 encoding";
345 return;
346 }
347 RefPtr<GraphicsContextSnapshot> snapshot = GraphicsContextSnapshot::load(snapshotData.data(), snapshotData.size());
348 if (!snapshot) {
349 *errorString = "Invalida snapshot format";
350 return;
351 }
352 *snapshotId = String::number(++s_lastSnapshotId);
353 bool newEntry = m_snapshotById.add(*snapshotId, snapshot).isNewEntry;
354 ASSERT_UNUSED(newEntry, newEntry);
355 }
356
releaseSnapshot(ErrorString * errorString,const String & snapshotId)357 void InspectorLayerTreeAgent::releaseSnapshot(ErrorString* errorString, const String& snapshotId)
358 {
359 SnapshotById::iterator it = m_snapshotById.find(snapshotId);
360 if (it == m_snapshotById.end()) {
361 *errorString = "Snapshot not found";
362 return;
363 }
364 m_snapshotById.remove(it);
365 }
366
snapshotById(ErrorString * errorString,const String & snapshotId)367 const GraphicsContextSnapshot* InspectorLayerTreeAgent::snapshotById(ErrorString* errorString, const String& snapshotId)
368 {
369 SnapshotById::iterator it = m_snapshotById.find(snapshotId);
370 if (it == m_snapshotById.end()) {
371 *errorString = "Snapshot not found";
372 return 0;
373 }
374 return it->value.get();
375 }
376
replaySnapshot(ErrorString * errorString,const String & snapshotId,const int * fromStep,const int * toStep,const double * scale,String * dataURL)377 void InspectorLayerTreeAgent::replaySnapshot(ErrorString* errorString, const String& snapshotId, const int* fromStep, const int* toStep, const double* scale, String* dataURL)
378 {
379 const GraphicsContextSnapshot* snapshot = snapshotById(errorString, snapshotId);
380 if (!snapshot)
381 return;
382 OwnPtr<Vector<char> > base64Data = snapshot->replay(fromStep ? *fromStep : 0, toStep ? *toStep : 0, scale ? *scale : 1.0);
383 if (!base64Data) {
384 *errorString = "Image encoding failed";
385 return;
386 }
387 StringBuilder url;
388 url.appendLiteral("data:image/png;base64,");
389 url.reserveCapacity(url.length() + base64Data->size());
390 url.append(base64Data->begin(), base64Data->size());
391 *dataURL = url.toString();
392 }
393
profileSnapshot(ErrorString * errorString,const String & snapshotId,const int * minRepeatCount,const double * minDuration,RefPtr<TypeBuilder::Array<TypeBuilder::Array<double>>> & outTimings)394 void InspectorLayerTreeAgent::profileSnapshot(ErrorString* errorString, const String& snapshotId, const int* minRepeatCount, const double* minDuration, RefPtr<TypeBuilder::Array<TypeBuilder::Array<double> > >& outTimings)
395 {
396 const GraphicsContextSnapshot* snapshot = snapshotById(errorString, snapshotId);
397 if (!snapshot)
398 return;
399 OwnPtr<GraphicsContextSnapshot::Timings> timings = snapshot->profile(minRepeatCount ? *minRepeatCount : 1, minDuration ? *minDuration : 0);
400 outTimings = TypeBuilder::Array<TypeBuilder::Array<double> >::create();
401 for (size_t i = 0; i < timings->size(); ++i) {
402 const Vector<double>& row = (*timings)[i];
403 RefPtr<TypeBuilder::Array<double> > outRow = TypeBuilder::Array<double>::create();
404 for (size_t j = 0; j < row.size(); ++j)
405 outRow->addItem(row[j]);
406 outTimings->addItem(outRow.release());
407 }
408 }
409
snapshotCommandLog(ErrorString * errorString,const String & snapshotId,RefPtr<TypeBuilder::Array<JSONObject>> & commandLog)410 void InspectorLayerTreeAgent::snapshotCommandLog(ErrorString* errorString, const String& snapshotId, RefPtr<TypeBuilder::Array<JSONObject> >& commandLog)
411 {
412 const GraphicsContextSnapshot* snapshot = snapshotById(errorString, snapshotId);
413 if (!snapshot)
414 return;
415 commandLog = TypeBuilder::Array<JSONObject>::runtimeCast(snapshot->snapshotCommandLog());
416 }
417
willAddPageOverlay(const GraphicsLayer * layer)418 void InspectorLayerTreeAgent::willAddPageOverlay(const GraphicsLayer* layer)
419 {
420 m_pageOverlayLayerIds.append(layer->platformLayer()->id());
421 }
422
didRemovePageOverlay(const GraphicsLayer * layer)423 void InspectorLayerTreeAgent::didRemovePageOverlay(const GraphicsLayer* layer)
424 {
425 size_t index = m_pageOverlayLayerIds.find(layer->platformLayer()->id());
426 if (index == WTF::kNotFound)
427 return;
428 m_pageOverlayLayerIds.remove(index);
429 }
430
431
432 } // namespace blink
433