Home | History | Annotate | Download | only in sdk
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /** @typedef {!{
     32         bounds: {height: number, width: number},
     33         children: Array.<!WebInspector.TracingLayerPayload>,
     34         layer_id: number,
     35         position: Array.<number>,
     36         scroll_offset: Array.<number>,
     37         layer_quad: Array.<number>,
     38         draws_content: number,
     39         transform: Array.<number>,
     40         owner_node: number
     41     }}
     42 */
     43 WebInspector.TracingLayerPayload;
     44 
     45 /**
     46   * @constructor
     47   * @extends {WebInspector.TargetAwareObject}
     48   */
     49 WebInspector.LayerTreeModel = function(target)
     50 {
     51     WebInspector.TargetAwareObject.call(this, target);
     52     InspectorBackend.registerLayerTreeDispatcher(new WebInspector.LayerTreeDispatcher(this));
     53     target.domModel.addEventListener(WebInspector.DOMModel.Events.DocumentUpdated, this._onDocumentUpdated, this);
     54     /** @type {?WebInspector.LayerTreeBase} */
     55     this._layerTree = null;
     56 }
     57 
     58 WebInspector.LayerTreeModel.Events = {
     59     LayerTreeChanged: "LayerTreeChanged",
     60     LayerPainted: "LayerPainted",
     61 }
     62 
     63 WebInspector.LayerTreeModel.ScrollRectType = {
     64     NonFastScrollable: {name: "NonFastScrollable", description: "Non fast scrollable"},
     65     TouchEventHandler: {name: "TouchEventHandler", description: "Touch event handler"},
     66     WheelEventHandler: {name: "WheelEventHandler", description: "Wheel event handler"},
     67     RepaintsOnScroll: {name: "RepaintsOnScroll", description: "Repaints on scroll"}
     68 }
     69 
     70 WebInspector.LayerTreeModel.prototype = {
     71     disable: function()
     72     {
     73         if (!this._enabled)
     74             return;
     75         this._enabled = false;
     76         this._layerTree = null;
     77         LayerTreeAgent.disable();
     78     },
     79 
     80     enable: function()
     81     {
     82         if (this._enabled)
     83             return;
     84         this._enabled = true;
     85         this._layerTree = new WebInspector.AgentLayerTree(this._target);
     86         this._lastPaintRectByLayerId = {};
     87         LayerTreeAgent.enable();
     88     },
     89 
     90     /**
     91      * @param {!WebInspector.LayerTreeBase} layerTree
     92      */
     93     setLayerTree: function(layerTree)
     94     {
     95         this.disable();
     96         this._layerTree = layerTree;
     97         this.dispatchEventToListeners(WebInspector.LayerTreeModel.Events.LayerTreeChanged);
     98     },
     99 
    100     /**
    101      * @return {?WebInspector.LayerTreeBase}
    102      */
    103     layerTree: function()
    104     {
    105         return this._layerTree;
    106     },
    107 
    108     /**
    109      * @param {?Array.<!LayerTreeAgent.Layer>} layers
    110      */
    111     _layerTreeChanged: function(layers)
    112     {
    113         if (!this._enabled)
    114             return;
    115         var layerTree = /** @type {!WebInspector.AgentLayerTree} */ (this._layerTree);
    116         layerTree.setLayers(layers, onLayersSet.bind(this));
    117 
    118         /**
    119          * @this {WebInspector.LayerTreeModel}
    120          */
    121         function onLayersSet()
    122         {
    123             for (var layerId in this._lastPaintRectByLayerId) {
    124                 var lastPaintRect = this._lastPaintRectByLayerId[layerId];
    125                 var layer = layerTree.layerById(layerId);
    126                 if (layer)
    127                     layer._lastPaintRect = lastPaintRect;
    128             }
    129             this._lastPaintRectByLayerId = {};
    130 
    131             this.dispatchEventToListeners(WebInspector.LayerTreeModel.Events.LayerTreeChanged);
    132         }
    133     },
    134 
    135     /**
    136      * @param {!LayerTreeAgent.LayerId} layerId
    137      * @param {!DOMAgent.Rect} clipRect
    138      */
    139     _layerPainted: function(layerId, clipRect)
    140     {
    141         if (!this._enabled)
    142             return;
    143         var layerTree = /** @type {!WebInspector.AgentLayerTree} */ (this._layerTree);
    144         var layer = layerTree.layerById(layerId);
    145         if (!layer) {
    146             this._lastPaintRectByLayerId[layerId] = clipRect;
    147             return;
    148         }
    149         layer._didPaint(clipRect);
    150         this.dispatchEventToListeners(WebInspector.LayerTreeModel.Events.LayerPainted, layer);
    151     },
    152 
    153     _onDocumentUpdated: function()
    154     {
    155         if (!this._enabled)
    156             return;
    157         this.disable();
    158         this.enable();
    159     },
    160 
    161     __proto__: WebInspector.TargetAwareObject.prototype
    162 }
    163 
    164 /**
    165   * @constructor
    166   * @extends {WebInspector.TargetAwareObject}
    167   * @param {!WebInspector.Target} target
    168   */
    169 WebInspector.LayerTreeBase = function(target)
    170 {
    171     WebInspector.TargetAwareObject.call(this, target);
    172     this._layersById = {};
    173     this._backendNodeIdToNodeId = {};
    174     this._reset();
    175 }
    176 
    177 WebInspector.LayerTreeBase.prototype = {
    178     _reset: function()
    179     {
    180         this._root = null;
    181         this._contentRoot = null;
    182     },
    183 
    184     /**
    185      * @return {?WebInspector.Layer}
    186      */
    187     root: function()
    188     {
    189         return this._root;
    190     },
    191 
    192     /**
    193      * @return {?WebInspector.Layer}
    194      */
    195     contentRoot: function()
    196     {
    197         return this._contentRoot;
    198     },
    199 
    200     /**
    201      * @param {function(!WebInspector.Layer)} callback
    202      * @param {?WebInspector.Layer=} root
    203      * @return {boolean}
    204      */
    205     forEachLayer: function(callback, root)
    206     {
    207         if (!root) {
    208             root = this.root();
    209             if (!root)
    210                 return false;
    211         }
    212         return callback(root) || root.children().some(this.forEachLayer.bind(this, callback));
    213     },
    214 
    215     /**
    216      * @param {string} id
    217      * @return {?WebInspector.Layer}
    218      */
    219     layerById: function(id)
    220     {
    221         return this._layersById[id] || null;
    222     },
    223 
    224     /**
    225      * @param {!Array.<number>} requestedNodeIds
    226      * @param {function()} callback
    227      */
    228     _resolveBackendNodeIds: function(requestedNodeIds, callback)
    229     {
    230         if (!requestedNodeIds.length) {
    231             callback();
    232             return;
    233         }
    234 
    235         this.target().domModel.pushNodesByBackendIdsToFrontend(requestedNodeIds, populateBackendNodeIdMap.bind(this));
    236 
    237         /**
    238          * @this {WebInspector.LayerTreeBase}
    239          * @param {?Array.<number>} nodeIds
    240          */
    241         function populateBackendNodeIdMap(nodeIds)
    242         {
    243             if (nodeIds) {
    244                 for (var i = 0; i < requestedNodeIds.length; ++i) {
    245                     var nodeId = nodeIds[i];
    246                     if (nodeId)
    247                         this._backendNodeIdToNodeId[requestedNodeIds[i]] = nodeId;
    248                 }
    249             }
    250             callback();
    251         }
    252     },
    253 
    254     /**
    255      * @param {!Object} viewportSize
    256      */
    257     setViewportSize: function(viewportSize)
    258     {
    259         this._viewportSize = viewportSize;
    260     },
    261 
    262     /**
    263      * @return {!Object | undefined}
    264      */
    265     viewportSize: function()
    266     {
    267         return this._viewportSize;
    268     },
    269 
    270     __proto__: WebInspector.TargetAwareObject.prototype
    271 };
    272 
    273 /**
    274   * @constructor
    275   * @extends {WebInspector.LayerTreeBase}
    276   * @param {!WebInspector.Target} target
    277   */
    278 WebInspector.TracingLayerTree = function(target)
    279 {
    280     WebInspector.LayerTreeBase.call(this, target);
    281 }
    282 
    283 WebInspector.TracingLayerTree.prototype = {
    284     /**
    285      * @param {!WebInspector.TracingLayerPayload} root
    286      * @param {!function()} callback
    287      */
    288     setLayers: function(root, callback)
    289     {
    290         var idsToResolve = [];
    291         this._extractNodeIdsToResolve(idsToResolve, {}, root);
    292         this._resolveBackendNodeIds(idsToResolve, onBackendNodeIdsResolved.bind(this));
    293 
    294         /**
    295          * @this {WebInspector.TracingLayerTree}
    296          */
    297         function onBackendNodeIdsResolved()
    298         {
    299             var oldLayersById = this._layersById;
    300             this._layersById = {};
    301             this._contentRoot = null;
    302             this._root = this._innerSetLayers(oldLayersById, root);
    303             callback();
    304         }
    305     },
    306 
    307     /**
    308      * @param {!Object.<(string|number), !WebInspector.Layer>} oldLayersById
    309      * @param {!WebInspector.TracingLayerPayload} payload
    310      * @return {!WebInspector.TracingLayer}
    311      */
    312     _innerSetLayers: function(oldLayersById, payload)
    313     {
    314         var layer = /** @type {?WebInspector.TracingLayer} */ (oldLayersById[payload.layer_id]);
    315         if (layer)
    316             layer._reset(payload);
    317         else
    318             layer = new WebInspector.TracingLayer(payload);
    319         this._layersById[payload.layer_id] = layer;
    320         if (!this._contentRoot && payload.draws_content)
    321             this._contentRoot = layer;
    322 
    323         if (payload.owner_node && this._backendNodeIdToNodeId[payload.owner_node])
    324             layer._setNode(this._target.domModel.nodeForId(this._backendNodeIdToNodeId[payload.owner_node]));
    325 
    326         for (var i = 0; payload.children && i < payload.children.length; ++i)
    327             layer.addChild(this._innerSetLayers(oldLayersById, payload.children[i]));
    328         return layer;
    329     },
    330 
    331     /**
    332      * @param {!Array.<number>} nodeIdsToResolve
    333      * @param {!Object} seenNodeIds
    334      * @param {!WebInspector.TracingLayerPayload} payload
    335      */
    336     _extractNodeIdsToResolve: function(nodeIdsToResolve, seenNodeIds, payload)
    337     {
    338         var backendNodeId = payload.owner_node;
    339         if (backendNodeId && !seenNodeIds[backendNodeId] && !(this._backendNodeIdToNodeId[backendNodeId] && this.target().domModel.nodeForId(backendNodeId))) {
    340             seenNodeIds[backendNodeId] = true;
    341             nodeIdsToResolve.push(backendNodeId);
    342         }
    343         for (var i = 0; payload.children && i < payload.children.length; ++i)
    344             this._extractNodeIdsToResolve(nodeIdsToResolve, seenNodeIds, payload.children[i]);
    345     },
    346 
    347     __proto__: WebInspector.LayerTreeBase.prototype
    348 }
    349 
    350 /**
    351   * @constructor
    352   * @extends {WebInspector.LayerTreeBase}
    353   */
    354 WebInspector.AgentLayerTree = function(target)
    355 {
    356     WebInspector.LayerTreeBase.call(this, target);
    357 }
    358 
    359 WebInspector.AgentLayerTree.prototype = {
    360     /**
    361      * @param {?Array.<!LayerTreeAgent.Layer>} payload
    362      * @param {function()} callback
    363      */
    364     setLayers: function(payload, callback)
    365     {
    366         if (!payload) {
    367             onBackendNodeIdsResolved.call(this);
    368             return;
    369         }
    370 
    371         var idsToResolve = {};
    372         var requestedIds = [];
    373         for (var i = 0; i < payload.length; ++i) {
    374             var backendNodeId = payload[i].backendNodeId;
    375             if (!backendNodeId || idsToResolve[backendNodeId] ||
    376                 (this._backendNodeIdToNodeId[backendNodeId] && this.target().domModel.nodeForId(this._backendNodeIdToNodeId[backendNodeId]))) {
    377                 continue;
    378             }
    379             idsToResolve[backendNodeId] = true;
    380             requestedIds.push(backendNodeId);
    381         }
    382         this._resolveBackendNodeIds(requestedIds, onBackendNodeIdsResolved.bind(this));
    383 
    384         /**
    385          * @this {WebInspector.AgentLayerTree}
    386          */
    387         function onBackendNodeIdsResolved()
    388         {
    389             this._innerSetLayers(payload);
    390             callback();
    391         }
    392     },
    393 
    394     /**
    395      * @param {?Array.<!LayerTreeAgent.Layer>} layers
    396      */
    397     _innerSetLayers: function(layers)
    398     {
    399         this._reset();
    400         // Payload will be null when not in the composited mode.
    401         if (!layers)
    402             return;
    403         var oldLayersById = this._layersById;
    404         this._layersById = {};
    405         for (var i = 0; i < layers.length; ++i) {
    406             var layerId = layers[i].layerId;
    407             var layer = oldLayersById[layerId];
    408             if (layer)
    409                 layer._reset(layers[i]);
    410             else
    411                 layer = new WebInspector.AgentLayer(layers[i]);
    412             this._layersById[layerId] = layer;
    413             if (layers[i].backendNodeId) {
    414                 layer._setNode(this._target.domModel.nodeForId(this._backendNodeIdToNodeId[layers[i].backendNodeId]));
    415                 if (!this._contentRoot)
    416                     this._contentRoot = layer;
    417             }
    418             var parentId = layer.parentId();
    419             if (parentId) {
    420                 var parent = this._layersById[parentId];
    421                 if (!parent)
    422                     console.assert(parent, "missing parent " + parentId + " for layer " + layerId);
    423                 parent.addChild(layer);
    424             } else {
    425                 if (this._root)
    426                     console.assert(false, "Multiple root layers");
    427                 this._root = layer;
    428             }
    429         }
    430         if (this._root)
    431             this._root._calculateQuad(new WebKitCSSMatrix());
    432     },
    433 
    434     __proto__: WebInspector.LayerTreeBase.prototype
    435 }
    436 
    437 /**
    438  * @interface
    439  */
    440 WebInspector.Layer = function()
    441 {
    442 }
    443 
    444 WebInspector.Layer.prototype = {
    445     /**
    446      * @return {string}
    447      */
    448     id: function() { },
    449 
    450     /**
    451      * @return {?string}
    452      */
    453     parentId: function() { },
    454 
    455     /**
    456      * @return {?WebInspector.Layer}
    457      */
    458     parent: function() { },
    459 
    460     /**
    461      * @return {boolean}
    462      */
    463     isRoot: function() { },
    464 
    465     /**
    466      * @return {!Array.<!WebInspector.Layer>}
    467      */
    468     children: function() { },
    469 
    470     /**
    471      * @param {!WebInspector.Layer} child
    472      */
    473     addChild: function(child) { },
    474 
    475     /**
    476      * @return {?WebInspector.DOMNode}
    477      */
    478     node: function() { },
    479 
    480     /**
    481      * @return {?WebInspector.DOMNode}
    482      */
    483     nodeForSelfOrAncestor: function() { },
    484 
    485     /**
    486      * @return {number}
    487      */
    488     offsetX: function() { },
    489 
    490     /**
    491      * @return {number}
    492      */
    493     offsetY: function() { },
    494 
    495     /**
    496      * @return {number}
    497      */
    498     width: function() { },
    499 
    500     /**
    501      * @return {number}
    502      */
    503     height: function() { },
    504 
    505     /**
    506      * @return {?Array.<number>}
    507      */
    508     transform: function() { },
    509 
    510     /**
    511      * @return {!Array.<number>}
    512      */
    513     quad: function() { },
    514 
    515     /**
    516      * @return {!Array.<number>}
    517      */
    518     anchorPoint: function() { },
    519 
    520     /**
    521      * @return {boolean}
    522      */
    523     invisible: function() { },
    524 
    525     /**
    526      * @return {number}
    527      */
    528     paintCount: function() { },
    529 
    530     /**
    531      * @return {?DOMAgent.Rect}
    532      */
    533     lastPaintRect: function() { },
    534 
    535     /**
    536      * @return {!Array.<!LayerTreeAgent.ScrollRect>}
    537      */
    538     scrollRects: function() { },
    539 
    540     /**
    541      * @param {function(!Array.<string>)} callback
    542      */
    543     requestCompositingReasons: function(callback) { },
    544 
    545     /**
    546      * @param {function(!WebInspector.PaintProfilerSnapshot=)} callback
    547      */
    548     requestSnapshot: function(callback) { },
    549 }
    550 
    551 /**
    552  * @constructor
    553  * @implements {WebInspector.Layer}
    554  * @param {!LayerTreeAgent.Layer} layerPayload
    555  */
    556 WebInspector.AgentLayer = function(layerPayload)
    557 {
    558     this._reset(layerPayload);
    559 }
    560 
    561 WebInspector.AgentLayer.prototype = {
    562     /**
    563      * @return {string}
    564      */
    565     id: function()
    566     {
    567         return this._layerPayload.layerId;
    568     },
    569 
    570     /**
    571      * @return {?string}
    572      */
    573     parentId: function()
    574     {
    575         return this._layerPayload.parentLayerId;
    576     },
    577 
    578     /**
    579      * @return {?WebInspector.Layer}
    580      */
    581     parent: function()
    582     {
    583         return this._parent;
    584     },
    585 
    586     /**
    587      * @return {boolean}
    588      */
    589     isRoot: function()
    590     {
    591         return !this.parentId();
    592     },
    593 
    594     /**
    595      * @return {!Array.<!WebInspector.Layer>}
    596      */
    597     children: function()
    598     {
    599         return this._children;
    600     },
    601 
    602     /**
    603      * @param {!WebInspector.Layer} child
    604      */
    605     addChild: function(child)
    606     {
    607         if (child._parent)
    608             console.assert(false, "Child already has a parent");
    609         this._children.push(child);
    610         child._parent = this;
    611     },
    612 
    613     /**
    614      * @param {?WebInspector.DOMNode} node
    615      */
    616     _setNode: function(node)
    617     {
    618         this._node = node;
    619     },
    620 
    621     /**
    622      * @return {?WebInspector.DOMNode}
    623      */
    624     node: function()
    625     {
    626         return this._node;
    627     },
    628 
    629     /**
    630      * @return {?WebInspector.DOMNode}
    631      */
    632     nodeForSelfOrAncestor: function()
    633     {
    634         for (var layer = this; layer; layer = layer._parent) {
    635             if (layer._node)
    636                 return layer._node;
    637         }
    638         return null;
    639     },
    640 
    641     /**
    642      * @return {number}
    643      */
    644     offsetX: function()
    645     {
    646         return this._layerPayload.offsetX;
    647     },
    648 
    649     /**
    650      * @return {number}
    651      */
    652     offsetY: function()
    653     {
    654         return this._layerPayload.offsetY;
    655     },
    656 
    657     /**
    658      * @return {number}
    659      */
    660     width: function()
    661     {
    662         return this._layerPayload.width;
    663     },
    664 
    665     /**
    666      * @return {number}
    667      */
    668     height: function()
    669     {
    670         return this._layerPayload.height;
    671     },
    672 
    673     /**
    674      * @return {?Array.<number>}
    675      */
    676     transform: function()
    677     {
    678         return this._layerPayload.transform;
    679     },
    680 
    681     /**
    682      * @return {!Array.<number>}
    683      */
    684     quad: function()
    685     {
    686         return this._quad;
    687     },
    688 
    689     /**
    690      * @return {!Array.<number>}
    691      */
    692     anchorPoint: function()
    693     {
    694         return [
    695             this._layerPayload.anchorX || 0,
    696             this._layerPayload.anchorY || 0,
    697             this._layerPayload.anchorZ || 0,
    698         ];
    699     },
    700 
    701     /**
    702      * @return {boolean}
    703      */
    704     invisible: function()
    705     {
    706         return this._layerPayload.invisible;
    707     },
    708 
    709     /**
    710      * @return {number}
    711      */
    712     paintCount: function()
    713     {
    714         return this._paintCount || this._layerPayload.paintCount;
    715     },
    716 
    717     /**
    718      * @return {?DOMAgent.Rect}
    719      */
    720     lastPaintRect: function()
    721     {
    722         return this._lastPaintRect;
    723     },
    724 
    725     /**
    726      * @return {!Array.<!LayerTreeAgent.ScrollRect>}
    727      */
    728     scrollRects: function()
    729     {
    730         return this._scrollRects;
    731     },
    732 
    733     /**
    734      * @param {function(!Array.<string>)} callback
    735      */
    736     requestCompositingReasons: function(callback)
    737     {
    738         var wrappedCallback = InspectorBackend.wrapClientCallback(callback, "LayerTreeAgent.reasonsForCompositingLayer(): ", undefined, []);
    739         LayerTreeAgent.compositingReasons(this.id(), wrappedCallback);
    740     },
    741 
    742     /**
    743      * @param {function(!WebInspector.PaintProfilerSnapshot=)} callback
    744      */
    745     requestSnapshot: function(callback)
    746     {
    747         var wrappedCallback = InspectorBackend.wrapClientCallback(callback, "LayerTreeAgent.makeSnapshot(): ", WebInspector.PaintProfilerSnapshot);
    748         LayerTreeAgent.makeSnapshot(this.id(), wrappedCallback);
    749     },
    750 
    751     /**
    752      * @param {!DOMAgent.Rect} rect
    753      */
    754     _didPaint: function(rect)
    755     {
    756         this._lastPaintRect = rect;
    757         this._paintCount = this.paintCount() + 1;
    758         this._image = null;
    759     },
    760 
    761     /**
    762      * @param {!LayerTreeAgent.Layer} layerPayload
    763      */
    764     _reset: function(layerPayload)
    765     {
    766         /** @type {?WebInspector.DOMNode} */
    767         this._node = null;
    768         this._children = [];
    769         this._parent = null;
    770         this._paintCount = 0;
    771         this._layerPayload = layerPayload;
    772         this._image = null;
    773         this._scrollRects = this._layerPayload.scrollRects || [];
    774     },
    775 
    776     /**
    777      * @param {!Array.<number>} a
    778      * @return {!CSSMatrix}
    779      */
    780     _matrixFromArray: function(a)
    781     {
    782         function toFixed9(x) { return x.toFixed(9); }
    783         return new WebKitCSSMatrix("matrix3d(" + a.map(toFixed9).join(",") + ")");
    784     },
    785 
    786     /**
    787      * @param {!CSSMatrix} parentTransform
    788      * @return {!CSSMatrix}
    789      */
    790     _calculateTransformToViewport: function(parentTransform)
    791     {
    792         var offsetMatrix = new WebKitCSSMatrix().translate(this._layerPayload.offsetX, this._layerPayload.offsetY);
    793         var matrix = offsetMatrix;
    794 
    795         if (this._layerPayload.transform) {
    796             var transformMatrix = this._matrixFromArray(this._layerPayload.transform);
    797             var anchorVector = new WebInspector.Geometry.Vector(this._layerPayload.width * this.anchorPoint()[0], this._layerPayload.height * this.anchorPoint()[1], this.anchorPoint()[2]);
    798             var anchorPoint = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(anchorVector, matrix);
    799             var anchorMatrix = new WebKitCSSMatrix().translate(-anchorPoint.x, -anchorPoint.y, -anchorPoint.z);
    800             matrix = anchorMatrix.inverse().multiply(transformMatrix.multiply(anchorMatrix.multiply(matrix)));
    801         }
    802 
    803         matrix = parentTransform.multiply(matrix);
    804         return matrix;
    805     },
    806 
    807     /**
    808      * @param {number} width
    809      * @param {number} height
    810      * @return {!Array.<number>}
    811      */
    812     _createVertexArrayForRect: function(width, height)
    813     {
    814         return [0, 0, 0, width, 0, 0, width, height, 0, 0, height, 0];
    815     },
    816 
    817     /**
    818      * @param {!CSSMatrix} parentTransform
    819      */
    820     _calculateQuad: function(parentTransform)
    821     {
    822         var matrix = this._calculateTransformToViewport(parentTransform);
    823         this._quad = [];
    824         var vertices = this._createVertexArrayForRect(this._layerPayload.width, this._layerPayload.height);
    825         for (var i = 0; i < 4; ++i) {
    826             var point = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]), matrix);
    827             this._quad.push(point.x, point.y);
    828         }
    829 
    830         function calculateQuadForLayer(layer)
    831         {
    832             layer._calculateQuad(matrix);
    833         }
    834 
    835         this._children.forEach(calculateQuadForLayer);
    836     }
    837 }
    838 
    839 /**
    840  * @constructor
    841  * @param {!WebInspector.TracingLayerPayload} payload
    842  * @implements {WebInspector.Layer}
    843  */
    844 WebInspector.TracingLayer = function(payload)
    845 {
    846     this._reset(payload);
    847 }
    848 
    849 WebInspector.TracingLayer.prototype = {
    850     /**
    851      * @param {!WebInspector.TracingLayerPayload} payload
    852      */
    853     _reset: function(payload)
    854     {
    855         /** @type {?WebInspector.DOMNode} */
    856         this._node = null;
    857         this._layerId = String(payload.layer_id);
    858         this._offsetX = payload.position[0];
    859         this._offsetY = payload.position[1];
    860         this._width = payload.bounds.width;
    861         this._height = payload.bounds.height;
    862         this._children = [];
    863         this._parentLayerId = null;
    864         this._parent = null;
    865         this._quad = payload.layer_quad || [];
    866         this._createScrollRects(payload);
    867     },
    868 
    869     /**
    870      * @return {string}
    871      */
    872     id: function()
    873     {
    874         return this._layerId;
    875     },
    876 
    877     /**
    878      * @return {?string}
    879      */
    880     parentId: function()
    881     {
    882         return this._parentLayerId;
    883     },
    884 
    885     /**
    886      * @return {?WebInspector.Layer}
    887      */
    888     parent: function()
    889     {
    890         return this._parent;
    891     },
    892 
    893     /**
    894      * @return {boolean}
    895      */
    896     isRoot: function()
    897     {
    898         return !this.parentId();
    899     },
    900 
    901     /**
    902      * @return {!Array.<!WebInspector.Layer>}
    903      */
    904     children: function()
    905     {
    906         return this._children;
    907     },
    908 
    909     /**
    910      * @param {!WebInspector.Layer} child
    911      */
    912     addChild: function(child)
    913     {
    914         if (child._parent)
    915             console.assert(false, "Child already has a parent");
    916         this._children.push(child);
    917         child._parent = this;
    918         child._parentLayerId = this._layerId;
    919     },
    920 
    921 
    922     /**
    923      * @param {?WebInspector.DOMNode} node
    924      */
    925     _setNode: function(node)
    926     {
    927         this._node = node;
    928     },
    929 
    930     /**
    931      * @return {?WebInspector.DOMNode}
    932      */
    933     node: function()
    934     {
    935         return this._node;
    936     },
    937 
    938     /**
    939      * @return {?WebInspector.DOMNode}
    940      */
    941     nodeForSelfOrAncestor: function()
    942     {
    943         for (var layer = this; layer; layer = layer._parent) {
    944             if (layer._node)
    945                 return layer._node;
    946         }
    947         return null;
    948     },
    949 
    950     /**
    951      * @return {number}
    952      */
    953     offsetX: function()
    954     {
    955         return this._offsetX;
    956     },
    957 
    958     /**
    959      * @return {number}
    960      */
    961     offsetY: function()
    962     {
    963         return this._offsetY;
    964     },
    965 
    966     /**
    967      * @return {number}
    968      */
    969     width: function()
    970     {
    971         return this._width;
    972     },
    973 
    974     /**
    975      * @return {number}
    976      */
    977     height: function()
    978     {
    979         return this._height;
    980     },
    981 
    982     /**
    983      * @return {?Array.<number>}
    984      */
    985     transform: function()
    986     {
    987         return null;
    988     },
    989 
    990     /**
    991      * @return {!Array.<number>}
    992      */
    993     quad: function()
    994     {
    995         return this._quad;
    996     },
    997 
    998     /**
    999      * @return {!Array.<number>}
   1000      */
   1001     anchorPoint: function()
   1002     {
   1003         return [0.5, 0.5, 0];
   1004     },
   1005 
   1006     /**
   1007      * @return {boolean}
   1008      */
   1009     invisible: function()
   1010     {
   1011         return false;
   1012     },
   1013 
   1014     /**
   1015      * @return {number}
   1016      */
   1017     paintCount: function()
   1018     {
   1019         return 0;
   1020     },
   1021 
   1022     /**
   1023      * @return {?DOMAgent.Rect}
   1024      */
   1025     lastPaintRect: function()
   1026     {
   1027         return null;
   1028     },
   1029 
   1030     /**
   1031      * @return {!Array.<!LayerTreeAgent.ScrollRect>}
   1032      */
   1033     scrollRects: function()
   1034     {
   1035         return this._scrollRects;
   1036     },
   1037 
   1038     /**
   1039      * @param {!Array.<number>} params
   1040      * @param {string} type
   1041      * @return {!Object}
   1042      */
   1043     _scrollRectsFromParams: function(params, type)
   1044     {
   1045         return {rect: {x: params[0], y: params[1], width: params[2], height: params[3]}, type: type};
   1046     },
   1047 
   1048     /**
   1049      * @param {!WebInspector.TracingLayerPayload} payload
   1050      */
   1051     _createScrollRects: function(payload)
   1052     {
   1053         this._scrollRects = [];
   1054         if (payload.non_fast_scrollable_region)
   1055             this._scrollRects.push(this._scrollRectsFromParams(payload.non_fast_scrollable_region, WebInspector.LayerTreeModel.ScrollRectType.NonFastScrollable.name));
   1056         if (payload.touch_event_handler_region)
   1057             this._scrollRects.push(this._scrollRectsFromParams(payload.touch_event_handler_region, WebInspector.LayerTreeModel.ScrollRectType.TouchEventHandler.name));
   1058         if (payload.wheel_event_handler_region)
   1059             this._scrollRects.push(this._scrollRectsFromParams(payload.wheel_event_handler_region, WebInspector.LayerTreeModel.ScrollRectType.WheelEventHandler.name));
   1060         if (payload.scroll_event_handler_region)
   1061             this._scrollRects.push(this._scrollRectsFromParams(payload.scroll_event_handler_region, WebInspector.LayerTreeModel.ScrollRectType.RepaintsOnScroll.name));
   1062     },
   1063 
   1064     /**
   1065      * @param {function(!Array.<string>)} callback
   1066      */
   1067     requestCompositingReasons: function(callback)
   1068     {
   1069         var wrappedCallback = InspectorBackend.wrapClientCallback(callback, "LayerTreeAgent.reasonsForCompositingLayer(): ", undefined, []);
   1070         LayerTreeAgent.compositingReasons(this.id(), wrappedCallback);
   1071     },
   1072 
   1073     /**
   1074      * @param {function(!WebInspector.PaintProfilerSnapshot=)} callback
   1075      */
   1076     requestSnapshot: function(callback)
   1077     {
   1078         var wrappedCallback = InspectorBackend.wrapClientCallback(callback, "LayerTreeAgent.makeSnapshot(): ", WebInspector.PaintProfilerSnapshot);
   1079         LayerTreeAgent.makeSnapshot(this.id(), wrappedCallback);
   1080     }
   1081 }
   1082 
   1083 /**
   1084  * @constructor
   1085  * @param {!WebInspector.Target} target
   1086  */
   1087 WebInspector.DeferredLayerTree = function(target)
   1088 {
   1089     this._target = target;
   1090 }
   1091 
   1092 WebInspector.DeferredLayerTree.prototype = {
   1093     /**
   1094      * @param {function(!WebInspector.LayerTreeBase)} callback
   1095      */
   1096     resolve: function(callback) { },
   1097 
   1098     /**
   1099      * @return {!WebInspector.Target}
   1100      */
   1101     target: function()
   1102     {
   1103         return this._target;
   1104     }
   1105 };
   1106 
   1107 /**
   1108  * @constructor
   1109  * @extends {WebInspector.DeferredLayerTree}
   1110  * @param {!WebInspector.Target} target
   1111  * @param {!Array.<!LayerTreeAgent.Layer>} layers
   1112  */
   1113 WebInspector.DeferredAgentLayerTree = function(target, layers)
   1114 {
   1115     WebInspector.DeferredLayerTree.call(this, target);
   1116     this._layers = layers;
   1117 }
   1118 
   1119 WebInspector.DeferredAgentLayerTree.prototype = {
   1120     /**
   1121      * @param {function(!WebInspector.LayerTreeBase)} callback
   1122      */
   1123     resolve: function(callback)
   1124     {
   1125         var result = new WebInspector.AgentLayerTree(this._target);
   1126         result.setLayers(this._layers, callback.bind(null, result));
   1127     },
   1128 
   1129     __proto__: WebInspector.DeferredLayerTree.prototype
   1130 };
   1131 
   1132 /**
   1133  * @constructor
   1134  * @extends {WebInspector.DeferredLayerTree}
   1135  * @param {!WebInspector.Target} target
   1136  * @param {!WebInspector.TracingLayerPayload} root
   1137  * @param {!Object} viewportSize
   1138  */
   1139 WebInspector.DeferredTracingLayerTree = function(target, root, viewportSize)
   1140 {
   1141     WebInspector.DeferredLayerTree.call(this, target);
   1142     this._root = root;
   1143     this._viewportSize = viewportSize;
   1144 }
   1145 
   1146 WebInspector.DeferredTracingLayerTree.prototype = {
   1147     /**
   1148      * @param {function(!WebInspector.LayerTreeBase)} callback
   1149      */
   1150     resolve: function(callback)
   1151     {
   1152         var result = new WebInspector.TracingLayerTree(this._target);
   1153         result.setViewportSize(this._viewportSize);
   1154         result.setLayers(this._root, callback.bind(null, result));
   1155     },
   1156 
   1157     __proto__: WebInspector.DeferredLayerTree.prototype
   1158 };
   1159 
   1160 /**
   1161  * @constructor
   1162  * @implements {LayerTreeAgent.Dispatcher}
   1163  * @param {!WebInspector.LayerTreeModel} layerTreeModel
   1164  */
   1165 WebInspector.LayerTreeDispatcher = function(layerTreeModel)
   1166 {
   1167     this._layerTreeModel = layerTreeModel;
   1168 }
   1169 
   1170 WebInspector.LayerTreeDispatcher.prototype = {
   1171     /**
   1172      * @param {!Array.<!LayerTreeAgent.Layer>=} layers
   1173      */
   1174     layerTreeDidChange: function(layers)
   1175     {
   1176         this._layerTreeModel._layerTreeChanged(layers || null);
   1177     },
   1178 
   1179     /**
   1180      * @param {!LayerTreeAgent.LayerId} layerId
   1181      * @param {!DOMAgent.Rect} clipRect
   1182      */
   1183     layerPainted: function(layerId, clipRect)
   1184     {
   1185         this._layerTreeModel._layerPainted(layerId, clipRect);
   1186     }
   1187 }
   1188