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