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