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 58namespace blink { 59 60unsigned InspectorLayerTreeAgent::s_lastSnapshotId; 61 62inline String idForLayer(const GraphicsLayer* graphicsLayer) 63{ 64 return String::number(graphicsLayer->platformLayer()->id()); 65} 66 67static 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 80static 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 97static 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 144InspectorLayerTreeAgent::InspectorLayerTreeAgent(Page* page) 145 : InspectorBaseAgent<InspectorLayerTreeAgent>("LayerTree") 146 , m_frontend(0) 147 , m_page(page) 148{ 149} 150 151InspectorLayerTreeAgent::~InspectorLayerTreeAgent() 152{ 153} 154 155void InspectorLayerTreeAgent::trace(Visitor* visitor) 156{ 157 visitor->trace(m_page); 158 InspectorBaseAgent::trace(visitor); 159} 160 161void InspectorLayerTreeAgent::setFrontend(InspectorFrontend* frontend) 162{ 163 m_frontend = frontend->layertree(); 164} 165 166void InspectorLayerTreeAgent::clearFrontend() 167{ 168 m_frontend = 0; 169 disable(0); 170} 171 172void 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 179void 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 189void InspectorLayerTreeAgent::disable(ErrorString*) 190{ 191 m_instrumentingAgents->setInspectorLayerTreeAgent(0); 192 m_snapshotById.clear(); 193 ErrorString unused; 194} 195 196void InspectorLayerTreeAgent::layerTreeDidChange() 197{ 198 m_frontend->layerTreeDidChange(buildLayerTree()); 199} 200 201void 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 215PassRefPtr<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 228void 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 247void 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 259int InspectorLayerTreeAgent::idForNode(Node* node) 260{ 261 return InspectorNodeIds::idForNode(node); 262} 263 264RenderLayerCompositor* InspectorLayerTreeAgent::renderLayerCompositor() 265{ 266 RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer(); 267 RenderLayerCompositor* compositor = renderView ? renderView->compositor() : 0; 268 return compositor; 269} 270 271static 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 286GraphicsLayer* 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 306void 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 324void 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 340void 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 357void 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 367const 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 377void 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 394void 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 410void 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 418void InspectorLayerTreeAgent::willAddPageOverlay(const GraphicsLayer* layer) 419{ 420 m_pageOverlayLayerIds.append(layer->platformLayer()->id()); 421} 422 423void 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