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