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