• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*/
43WebInspector.TracingLayerPayload;
44
45/**
46  * @constructor
47  * @extends {WebInspector.TargetAwareObject}
48  */
49WebInspector.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
58WebInspector.LayerTreeModel.Events = {
59    LayerTreeChanged: "LayerTreeChanged",
60    LayerPainted: "LayerPainted",
61}
62
63WebInspector.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
70WebInspector.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  */
169WebInspector.LayerTreeBase = function(target)
170{
171    WebInspector.TargetAwareObject.call(this, target);
172    this._layersById = {};
173    this._backendNodeIdToNodeId = {};
174    this._reset();
175}
176
177WebInspector.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  */
278WebInspector.TracingLayerTree = function(target)
279{
280    WebInspector.LayerTreeBase.call(this, target);
281}
282
283WebInspector.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  */
354WebInspector.AgentLayerTree = function(target)
355{
356    WebInspector.LayerTreeBase.call(this, target);
357}
358
359WebInspector.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 */
440WebInspector.Layer = function()
441{
442}
443
444WebInspector.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 */
556WebInspector.AgentLayer = function(layerPayload)
557{
558    this._reset(layerPayload);
559}
560
561WebInspector.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 */
844WebInspector.TracingLayer = function(payload)
845{
846    this._reset(payload);
847}
848
849WebInspector.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 */
1087WebInspector.DeferredLayerTree = function(target)
1088{
1089    this._target = target;
1090}
1091
1092WebInspector.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 */
1113WebInspector.DeferredAgentLayerTree = function(target, layers)
1114{
1115    WebInspector.DeferredLayerTree.call(this, target);
1116    this._layers = layers;
1117}
1118
1119WebInspector.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 */
1139WebInspector.DeferredTracingLayerTree = function(target, root, viewportSize)
1140{
1141    WebInspector.DeferredLayerTree.call(this, target);
1142    this._root = root;
1143    this._viewportSize = viewportSize;
1144}
1145
1146WebInspector.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 */
1165WebInspector.LayerTreeDispatcher = function(layerTreeModel)
1166{
1167    this._layerTreeModel = layerTreeModel;
1168}
1169
1170WebInspector.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