Home | History | Annotate | Download | only in timeline
      1 /*
      2  * Copyright (C) 2014 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 /**
     32  * @constructor
     33  * @extends {WebInspector.VBox}
     34  */
     35 WebInspector.Layers3DView = function()
     36 {
     37     WebInspector.VBox.call(this);
     38     this.element.classList.add("layers-3d-view");
     39     this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("Layer information is not yet available."));
     40 
     41     this._transformController = new WebInspector.TransformController(this.element);
     42     this._transformController.addEventListener(WebInspector.TransformController.Events.TransformChanged, this._update, this);
     43     this._initStatusBar();
     44 
     45     this._canvasElement = this.element.createChild("canvas");
     46     this._canvasElement.tabIndex = 0;
     47     this._canvasElement.addEventListener("dblclick", this._onDoubleClick.bind(this), false);
     48     this._canvasElement.addEventListener("mousedown", this._onMouseDown.bind(this), false);
     49     this._canvasElement.addEventListener("mouseup", this._onMouseUp.bind(this), false);
     50     this._canvasElement.addEventListener("mouseout", this._onMouseMove.bind(this), false);
     51     this._canvasElement.addEventListener("mousemove", this._onMouseMove.bind(this), false);
     52     this._canvasElement.addEventListener("contextmenu", this._onContextMenu.bind(this), false);
     53 
     54     this._lastActiveObject = {};
     55     this._picturesForLayer = {};
     56     this._scrollRectQuadsForLayer = {};
     57     this._isVisible = {};
     58     this._layerTree = null;
     59     this._textureManager = new WebInspector.LayerTextureManager();
     60     this._textureManager.addEventListener(WebInspector.LayerTextureManager.Events.TextureUpdated, this._update, this);
     61 
     62     WebInspector.settings.showPaintRects.addChangeListener(this._update, this);
     63 }
     64 
     65 /** @typedef {{borderColor: !Array.<number>, borderWidth: number}} */
     66 WebInspector.Layers3DView.LayerStyle;
     67 
     68 /** @typedef {{layerId: string, rect: !Array.<number>, snapshot: !WebInspector.PaintProfilerSnapshot, traceEvent: !WebInspector.TracingModel.Event}} */
     69 WebInspector.Layers3DView.PaintTile;
     70 
     71 /**
     72  * @enum {string}
     73  */
     74 WebInspector.Layers3DView.OutlineType = {
     75     Hovered: "hovered",
     76     Selected: "selected"
     77 }
     78 
     79 /**
     80  * @enum {string}
     81  */
     82 WebInspector.Layers3DView.Events = {
     83     ObjectHovered: "ObjectHovered",
     84     ObjectSelected: "ObjectSelected",
     85     LayerSnapshotRequested: "LayerSnapshotRequested",
     86     JumpToPaintEventRequested: "JumpToPaintEventRequested"
     87 }
     88 
     89 /**
     90  * @enum {string}
     91  */
     92 WebInspector.Layers3DView.ScrollRectTitles = {
     93     RepaintsOnScroll: WebInspector.UIString("repaints on scroll"),
     94     TouchEventHandler: WebInspector.UIString("touch event listener"),
     95     WheelEventHandler: WebInspector.UIString("mousewheel event listener")
     96 }
     97 
     98 WebInspector.Layers3DView.FragmentShader = "\
     99     precision mediump float;\
    100     varying vec4 vColor;\
    101     varying vec2 vTextureCoord;\
    102     uniform sampler2D uSampler;\
    103     void main(void)\
    104     {\
    105         gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)) * vColor;\
    106     }";
    107 
    108 WebInspector.Layers3DView.VertexShader = "\
    109     attribute vec3 aVertexPosition;\
    110     attribute vec2 aTextureCoord;\
    111     attribute vec4 aVertexColor;\
    112     uniform mat4 uPMatrix;\
    113     varying vec2 vTextureCoord;\
    114     varying vec4 vColor;\
    115     void main(void)\
    116     {\
    117         gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);\
    118         vColor = aVertexColor;\
    119         vTextureCoord = aTextureCoord;\
    120     }";
    121 
    122 WebInspector.Layers3DView.SelectedBackgroundColor = [20, 40, 110, 0.66];
    123 WebInspector.Layers3DView.BackgroundColor = [0, 0, 0, 0];
    124 WebInspector.Layers3DView.HoveredBorderColor = [0, 0, 255, 1];
    125 WebInspector.Layers3DView.SelectedBorderColor = [0, 255, 0, 1];
    126 WebInspector.Layers3DView.BorderColor = [0, 0, 0, 1];
    127 WebInspector.Layers3DView.ScrollRectBackgroundColor = [178, 0, 0, 0.4];
    128 WebInspector.Layers3DView.SelectedScrollRectBackgroundColor = [178, 0, 0, 0.6];
    129 WebInspector.Layers3DView.ScrollRectBorderColor = [178, 0, 0, 1];
    130 WebInspector.Layers3DView.BorderWidth = 1;
    131 WebInspector.Layers3DView.SelectedBorderWidth = 2;
    132 
    133 WebInspector.Layers3DView.LayerSpacing = 20;
    134 WebInspector.Layers3DView.ScrollRectSpacing = 4;
    135 
    136 WebInspector.Layers3DView.prototype = {
    137     /**
    138      * @param {?WebInspector.LayerTreeBase} layerTree
    139      */
    140     setLayerTree: function(layerTree)
    141     {
    142         this._layerTree = layerTree;
    143         this._textureManager.reset();
    144         this._update();
    145     },
    146 
    147     /**
    148      * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} tiles
    149      */
    150     setTiles: function(tiles)
    151     {
    152         this._textureManager.setTiles(tiles);
    153     },
    154 
    155     /**
    156      * @param {!WebInspector.Layer} layer
    157      * @param {string=} imageURL
    158      */
    159     showImageForLayer: function(layer, imageURL)
    160     {
    161         this._textureManager.createTexture(onTextureCreated.bind(this), imageURL);
    162 
    163         /**
    164          * @this {WebInspector.Layers3DView}
    165          * @param {!WebGLTexture} texture
    166          */
    167         function onTextureCreated(texture)
    168         {
    169             this._layerTexture = {layerId: layer.id(), texture: texture};
    170             this._update();
    171         }
    172     },
    173 
    174     onResize: function()
    175     {
    176         this._update();
    177     },
    178 
    179     wasShown: function()
    180     {
    181         if (this._needsUpdate)
    182             this._update();
    183     },
    184 
    185     /**
    186      * @param {!WebInspector.Layers3DView.OutlineType} type
    187      * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
    188      */
    189     _setOutline: function(type, activeObject)
    190     {
    191         this._lastActiveObject[type] = activeObject;
    192         this._update();
    193     },
    194 
    195     /**
    196      * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
    197      */
    198     hoverObject: function(activeObject)
    199     {
    200         this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, activeObject);
    201     },
    202 
    203     /**
    204      * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
    205      */
    206     selectObject: function(activeObject)
    207     {
    208         this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, null);
    209         this._setOutline(WebInspector.Layers3DView.OutlineType.Selected, activeObject);
    210     },
    211 
    212     /**
    213      * @param {!Element} canvas
    214      * @return {!WebGLRenderingContext}
    215      */
    216     _initGL: function(canvas)
    217     {
    218         var gl = canvas.getContext("webgl");
    219         gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    220         gl.enable(gl.BLEND);
    221         gl.clearColor(0.0, 0.0, 0.0, 0.0);
    222         gl.enable(gl.DEPTH_TEST);
    223         return gl;
    224     },
    225 
    226     /**
    227      * @param {!Object} type
    228      * @param {string} script
    229      */
    230     _createShader: function(type, script)
    231     {
    232         var shader = this._gl.createShader(type);
    233         this._gl.shaderSource(shader, script);
    234         this._gl.compileShader(shader);
    235         this._gl.attachShader(this._shaderProgram, shader);
    236     },
    237 
    238     _initShaders: function()
    239     {
    240         this._shaderProgram = this._gl.createProgram();
    241         this._createShader(this._gl.FRAGMENT_SHADER, WebInspector.Layers3DView.FragmentShader);
    242         this._createShader(this._gl.VERTEX_SHADER, WebInspector.Layers3DView.VertexShader);
    243         this._gl.linkProgram(this._shaderProgram);
    244         this._gl.useProgram(this._shaderProgram);
    245 
    246         this._shaderProgram.vertexPositionAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexPosition");
    247         this._gl.enableVertexAttribArray(this._shaderProgram.vertexPositionAttribute);
    248         this._shaderProgram.vertexColorAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexColor");
    249         this._gl.enableVertexAttribArray(this._shaderProgram.vertexColorAttribute);
    250         this._shaderProgram.textureCoordAttribute = this._gl.getAttribLocation(this._shaderProgram, "aTextureCoord");
    251         this._gl.enableVertexAttribArray(this._shaderProgram.textureCoordAttribute);
    252 
    253         this._shaderProgram.pMatrixUniform = this._gl.getUniformLocation(this._shaderProgram, "uPMatrix");
    254         this._shaderProgram.samplerUniform = this._gl.getUniformLocation(this._shaderProgram, "uSampler");
    255     },
    256 
    257     _resizeCanvas: function()
    258     {
    259         this._canvasElement.width = this._canvasElement.offsetWidth * window.devicePixelRatio;
    260         this._canvasElement.height = this._canvasElement.offsetHeight * window.devicePixelRatio;
    261         this._gl.viewportWidth = this._canvasElement.width;
    262         this._gl.viewportHeight = this._canvasElement.height;
    263     },
    264 
    265     /**
    266      * @return {!CSSMatrix}
    267      */
    268     _calculateProjectionMatrix: function()
    269     {
    270         var scaleFactorForMargins = 1.2;
    271         var viewport = this._layerTree.viewportSize();
    272         var baseWidth = viewport ? viewport.width : this._layerTree.contentRoot().width();
    273         var baseHeight = viewport ? viewport.height : this._layerTree.contentRoot().height();
    274         var canvasWidth = this._canvasElement.width;
    275         var canvasHeight = this._canvasElement.height;
    276         var scaleX = canvasWidth / baseWidth / scaleFactorForMargins;
    277         var scaleY = canvasHeight / baseHeight / scaleFactorForMargins;
    278         var viewScale = Math.min(scaleX, scaleY);
    279         var scale = this._transformController.scale();
    280         var offsetX = this._transformController.offsetX() * window.devicePixelRatio;
    281         var offsetY = this._transformController.offsetY() * window.devicePixelRatio;
    282         var rotateX = this._transformController.rotateX();
    283         var rotateY = this._transformController.rotateY();
    284         return new WebKitCSSMatrix().translate(offsetX, offsetY, 0).scale(scale, scale, scale).translate(canvasWidth / 2, canvasHeight / 2, 0)
    285             .rotate(rotateX, rotateY, 0).scale(viewScale, viewScale, viewScale).translate(-baseWidth / 2, -baseHeight / 2, 0);
    286     },
    287 
    288     /**
    289      * @param {!CSSMatrix} m
    290      * @return {!Float32Array}
    291      */
    292     _arrayFromMatrix: function(m)
    293     {
    294         return new Float32Array([m.m11, m.m12, m.m13, m.m14, m.m21, m.m22, m.m23, m.m24, m.m31, m.m32, m.m33, m.m34, m.m41, m.m42, m.m43, m.m44]);
    295     },
    296 
    297     _initProjectionMatrix: function()
    298     {
    299         var projectionMatrix = this._calculateProjectionMatrix();
    300         this._pMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0)
    301             .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(projectionMatrix);
    302         this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(this._pMatrix));
    303         this._textureScale = Math.min(1, Math.max(projectionMatrix.m11, projectionMatrix.m22));
    304     },
    305 
    306     _initWhiteTexture: function()
    307     {
    308         this._whiteTexture = this._gl.createTexture();
    309         this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture);
    310         var whitePixel = new Uint8Array([255, 255, 255, 255]);
    311         this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, 1, 1, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, whitePixel);
    312     },
    313 
    314     _initGLIfNecessary: function()
    315     {
    316         if (this._gl)
    317             return this._gl;
    318         this._gl = this._initGL(this._canvasElement);
    319         this._initShaders();
    320         this._initWhiteTexture();
    321         this._textureManager.setContext(this._gl);
    322         return this._gl;
    323     },
    324 
    325     _calculateDepths: function()
    326     {
    327         this._depthByLayerId = {};
    328         this._isVisible = {};
    329         var depth = 0;
    330         var root = this._layerTree.root();
    331         var queue = [root];
    332         this._depthByLayerId[root.id()] = 0;
    333         this._isVisible[root.id()] = false;
    334         while (queue.length > 0) {
    335             var layer = queue.shift();
    336             var children = layer.children();
    337             for (var i = 0; i < children.length; ++i) {
    338                 this._depthByLayerId[children[i].id()] = ++depth;
    339                 this._isVisible[children[i].id()] = children[i] === this._layerTree.contentRoot() || this._isVisible[layer.id()];
    340                 queue.push(children[i]);
    341             }
    342         }
    343         this._maxDepth = depth;
    344     },
    345 
    346     /**
    347      * @param {!WebInspector.Layers3DView.OutlineType} type
    348      * @param {!WebInspector.Layer} layer
    349      * @param {number=} scrollRectIndex
    350      */
    351     _isObjectActive: function(type, layer, scrollRectIndex)
    352     {
    353         var activeObject = this._lastActiveObject[type];
    354         return activeObject && activeObject.layer && activeObject.layer.id() === layer.id() && (typeof scrollRectIndex !== "number" || activeObject.scrollRectIndex === scrollRectIndex);
    355     },
    356 
    357     /**
    358      * @param {!WebInspector.Layer} layer
    359      * @return {!WebInspector.Layers3DView.LayerStyle}
    360      */
    361     _styleForLayer: function(layer)
    362     {
    363         var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer);
    364         var isHovered = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Hovered, layer);
    365         var borderColor;
    366         if (isSelected)
    367             borderColor = WebInspector.Layers3DView.SelectedBorderColor;
    368         else if (isHovered)
    369             borderColor = WebInspector.Layers3DView.HoveredBorderColor;
    370         else
    371             borderColor = WebInspector.Layers3DView.BorderColor;
    372         var borderWidth = isSelected ? WebInspector.Layers3DView.SelectedBorderWidth : WebInspector.Layers3DView.BorderWidth;
    373         return {borderColor: borderColor, borderWidth: borderWidth};
    374     },
    375 
    376     /**
    377      * @param {!WebInspector.Layer} layer
    378      * @return {number}
    379      */
    380     _depthForLayer: function(layer)
    381     {
    382         return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing;
    383     },
    384 
    385     /**
    386      * @param {!WebInspector.Layer} layer
    387      * @param {number} index
    388      * @return {number}
    389      */
    390     _calculateScrollRectDepth: function(layer, index)
    391     {
    392         return this._depthForLayer(layer) + index * WebInspector.Layers3DView.ScrollRectSpacing + 1;
    393     },
    394 
    395     /**
    396      * @param {!WebInspector.Layer} layer
    397      */
    398     _calculateLayerRect: function(layer)
    399     {
    400         if (!this._isVisible[layer.id()])
    401             return;
    402         var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer);
    403         var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
    404         var style = this._styleForLayer(layer);
    405         rect.setVertices(layer.quad(), this._depthForLayer(layer));
    406         rect.lineWidth = style.borderWidth;
    407         rect.borderColor = style.borderColor;
    408         this._rects.push(rect);
    409     },
    410 
    411     /**
    412      * @param {!WebInspector.Layer} layer
    413      */
    414     _calculateLayerScrollRects: function(layer)
    415     {
    416         var scrollRects = layer.scrollRects();
    417         for (var i = 0; i < scrollRects.length; ++i) {
    418             var activeObject = WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject(layer, i);
    419             var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
    420             rect.calculateVerticesFromRect(layer, scrollRects[i].rect, this._calculateScrollRectDepth(layer, i));
    421             var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i);
    422             var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor;
    423             rect.fillColor = color;
    424             rect.borderColor = WebInspector.Layers3DView.ScrollRectBorderColor;
    425             this._rects.push(rect);
    426         }
    427     },
    428 
    429     /**
    430      * @param {!WebInspector.Layer} layer
    431      */
    432     _calculateLayerImageRect: function(layer)
    433     {
    434         var layerTexture = this._layerTexture;
    435         if (layer.id() !== layerTexture.layerId)
    436             return;
    437         var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer);
    438         var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
    439         rect.setVertices(layer.quad(), this._depthForLayer(layer));
    440         rect.texture = layerTexture.texture;
    441         this._rects.push(rect);
    442     },
    443 
    444     /**
    445      * @param {!WebInspector.Layer} layer
    446      */
    447     _calculateLayerTileRects: function(layer)
    448     {
    449         var tiles = this._textureManager.tilesForLayer(layer.id());
    450         for (var i = 0; i < tiles.length; ++i) {
    451             var tile = tiles[i];
    452             if (!tile.texture)
    453                 continue;
    454             var activeObject = WebInspector.Layers3DView.ActiveObject.createTileActiveObject(layer, tile.traceEvent);
    455             var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
    456             rect.calculateVerticesFromRect(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2], height: tile.rect[3]}, this._depthForLayer(layer) + 1);
    457             rect.texture = tile.texture;
    458             this._rects.push(rect);
    459         }
    460     },
    461 
    462     _calculateViewportRect: function()
    463     {
    464         var rect = new WebInspector.Layers3DView.Rectangle(null);
    465         var viewport = this._layerTree.viewportSize();
    466         var depth = (this._maxDepth + 1) * WebInspector.Layers3DView.LayerSpacing;
    467         var vertices = [0, 0, depth, viewport.width, 0, depth, viewport.width, viewport.height, depth, 0, viewport.height, depth];
    468         rect.vertices = vertices;
    469         rect.borderColor = [0, 0, 0, 1];
    470         rect.lineWidth = 3;
    471         this._rects.push(rect);
    472     },
    473 
    474     _calculateRects: function()
    475     {
    476         this._rects = [];
    477 
    478         this._layerTree.forEachLayer(this._calculateLayerRect.bind(this));
    479 
    480         if (this._showSlowScrollRectsSetting.get())
    481             this._layerTree.forEachLayer(this._calculateLayerScrollRects.bind(this));
    482 
    483         if (this._showPaintsSetting.get()) {
    484             if (this._layerTexture)
    485                 this._layerTree.forEachLayer(this._calculateLayerImageRect.bind(this));
    486             else
    487                 this._layerTree.forEachLayer(this._calculateLayerTileRects.bind(this));
    488         }
    489 
    490         if (this._layerTree.viewportSize() && this._showViewportSetting.get())
    491             this._calculateViewportRect();
    492     },
    493 
    494     /**
    495      * @param {!Array.<number>} color
    496      * @return {!Array.<number>}
    497      */
    498     _makeColorsArray: function(color)
    499     {
    500         var colors = [];
    501         var normalizedColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3]];
    502         for (var i = 0; i < 4; i++) {
    503             colors = colors.concat(normalizedColor);
    504         }
    505         return colors;
    506     },
    507 
    508     /**
    509      * @param {!Object} attribute
    510      * @param {!Array.<number>} array
    511      * @param {!number} length
    512      */
    513     _setVertexAttribute: function(attribute, array, length)
    514     {
    515         var gl = this._gl;
    516         var buffer = gl.createBuffer();
    517         gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    518         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW);
    519         gl.vertexAttribPointer(attribute, length, gl.FLOAT, false, 0, 0);
    520     },
    521 
    522     /**
    523      * @param {!Array.<number>} vertices
    524      * @param {boolean} isBorder
    525      * @param {!Array.<number>=} color
    526      * @param {!Object=} texture
    527      */
    528     _drawRectangle: function(vertices, isBorder, color, texture)
    529     {
    530         var gl = this._gl;
    531         var glMode = isBorder ? gl.LINE_LOOP : gl.TRIANGLE_FAN;
    532         var white = [255, 255, 255, 1];
    533         this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3);
    534         this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2);
    535 
    536         if (texture) {
    537             this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length);
    538             gl.activeTexture(gl.TEXTURE0);
    539             gl.bindTexture(gl.TEXTURE_2D, texture);
    540             gl.uniform1i(this._shaderProgram.samplerUniform, 0);
    541         } else {
    542             this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color || white), color.length);
    543             gl.bindTexture(gl.TEXTURE_2D, this._whiteTexture);
    544         }
    545 
    546         var numberOfVertices = 4;
    547         gl.drawArrays(glMode, 0, numberOfVertices);
    548     },
    549 
    550     /**
    551      * @param {!WebInspector.Layers3DView.Rectangle} rect
    552      */
    553     _drawViewRect: function(rect)
    554     {
    555         var vertices = rect.vertices;
    556         if (rect.texture)
    557             this._drawRectangle(vertices, false, undefined, rect.texture);
    558         else if (rect.fillColor)
    559             this._drawRectangle(vertices, false, rect.fillColor);
    560         this._gl.lineWidth(rect.lineWidth);
    561         if (rect.borderColor)
    562             this._drawRectangle(vertices, true, rect.borderColor);
    563     },
    564 
    565     _update: function()
    566     {
    567         if (!this.isShowing()) {
    568             this._needsUpdate = true;
    569             return;
    570         }
    571         var contentRoot = this._layerTree && this._layerTree.contentRoot();
    572         if (!contentRoot || !this._layerTree.root()) {
    573             this._emptyView.show(this.element);
    574             return;
    575         }
    576         this._emptyView.detach();
    577 
    578         var gl = this._initGLIfNecessary();
    579         this._resizeCanvas();
    580         this._initProjectionMatrix();
    581         this._calculateDepths();
    582 
    583         this._textureManager.setScale(this._textureScale);
    584         gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    585         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    586 
    587         this._calculateRects();
    588         this._rects.forEach(this._drawViewRect.bind(this));
    589     },
    590 
    591     /**
    592      * @param {!Event} event
    593      * @return {?WebInspector.Layers3DView.ActiveObject}
    594      */
    595     _activeObjectFromEventPoint: function(event)
    596     {
    597         if (!this._layerTree)
    598             return null;
    599         var closestIntersectionPoint = Infinity;
    600         var closestObject = null;
    601         var projectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0).multiply(this._calculateProjectionMatrix());
    602         var x0 = (event.clientX - this._canvasElement.totalOffsetLeft()) * window.devicePixelRatio;
    603         var y0 = -(event.clientY - this._canvasElement.totalOffsetTop()) * window.devicePixelRatio;
    604 
    605         /**
    606          * @param {!WebInspector.Layers3DView.Rectangle} rect
    607          */
    608         function checkIntersection(rect)
    609         {
    610             if (!rect.relatedObject)
    611                 return;
    612             var t = rect.intersectWithLine(projectionMatrix, x0, y0);
    613             if (t < closestIntersectionPoint) {
    614                 closestIntersectionPoint = t;
    615                 closestObject = rect.relatedObject;
    616             }
    617         }
    618 
    619         this._rects.forEach(checkIntersection);
    620         return closestObject;
    621     },
    622 
    623     /**
    624      * @param {string} caption
    625      * @param {string} name
    626      * @param {boolean} value
    627      * @param {!Element} statusBarElement
    628      * @return {!WebInspector.Setting}
    629      */
    630     _createVisibilitySetting: function(caption, name, value, statusBarElement)
    631     {
    632         var checkbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString(caption))
    633         statusBarElement.appendChild(checkbox.element);
    634         var setting = WebInspector.settings.createSetting(name, value)
    635         WebInspector.SettingsUI.bindCheckbox(checkbox.inputElement, setting);
    636         setting.addChangeListener(this._update, this);
    637         return setting;
    638     },
    639 
    640     _initStatusBar: function()
    641     {
    642         this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
    643         this._panelStatusBarElement.appendChild(this._transformController.controlPanelElement());
    644         this._showViewportSetting = this._createVisibilitySetting("Viewport", "showViewport", true, this._panelStatusBarElement);
    645         this._showSlowScrollRectsSetting = this._createVisibilitySetting("Slow scroll rects", "showSlowScrollRects", true, this._panelStatusBarElement);
    646         this._showPaintsSetting = this._createVisibilitySetting("Paints", "showPaints", true, this._panelStatusBarElement);
    647     },
    648 
    649     /**
    650      * @param {!Event} event
    651      */
    652     _onContextMenu: function(event)
    653     {
    654         var activeObject = this._activeObjectFromEventPoint(event);
    655         var node = activeObject && activeObject.layer && activeObject.layer.nodeForSelfOrAncestor();
    656         var contextMenu = new WebInspector.ContextMenu(event);
    657         contextMenu.appendItem(WebInspector.UIString("Reset View"), this._transformController.resetAndNotify.bind(this._transformController), false);
    658         if (activeObject && activeObject.type() === WebInspector.Layers3DView.ActiveObject.Type.Tile)
    659             contextMenu.appendItem(WebInspector.UIString("Jump to Paint Event"), this.dispatchEventToListeners.bind(this, WebInspector.Layers3DView.Events.JumpToPaintEventRequested, activeObject.traceEvent), false);
    660         if (node)
    661             contextMenu.appendApplicableItems(node);
    662         contextMenu.show();
    663     },
    664 
    665     /**
    666      * @param {!Event} event
    667      */
    668     _onMouseMove: function(event)
    669     {
    670         if (event.which)
    671             return;
    672         this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectHovered, this._activeObjectFromEventPoint(event));
    673     },
    674 
    675     /**
    676      * @param {!Event} event
    677      */
    678     _onMouseDown: function(event)
    679     {
    680         this._mouseDownX = event.clientX;
    681         this._mouseDownY = event.clientY;
    682     },
    683 
    684     /**
    685      * @param {!Event} event
    686      */
    687     _onMouseUp: function(event)
    688     {
    689         const maxDistanceInPixels = 6;
    690         if (this._mouseDownX && Math.abs(event.clientX - this._mouseDownX) < maxDistanceInPixels && Math.abs(event.clientY - this._mouseDownY) < maxDistanceInPixels)
    691             this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectSelected, this._activeObjectFromEventPoint(event));
    692         delete this._mouseDownX;
    693         delete this._mouseDownY;
    694     },
    695 
    696     /**
    697      * @param {!Event} event
    698      */
    699     _onDoubleClick: function(event)
    700     {
    701         var object = this._activeObjectFromEventPoint(event);
    702         if (object) {
    703             if (object.type() == WebInspector.Layers3DView.ActiveObject.Type.Tile)
    704                 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.JumpToPaintEventRequested, object.traceEvent);
    705             else if (object.layer)
    706                 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer);
    707         }
    708         event.stopPropagation();
    709     },
    710 
    711     __proto__: WebInspector.VBox.prototype
    712 }
    713 
    714 /**
    715  * @constructor
    716  * @extends {WebInspector.Object}
    717  */
    718 WebInspector.LayerTextureManager = function()
    719 {
    720     WebInspector.Object.call(this);
    721     this.reset();
    722 }
    723 
    724 WebInspector.LayerTextureManager.Events = {
    725     TextureUpdated: "TextureUpated"
    726 }
    727 
    728 WebInspector.LayerTextureManager.prototype = {
    729     reset: function()
    730     {
    731         /** @type {!Object.<string, !Array.<!WebInspector.LayerTextureManager.Tile>>} */
    732         this._tilesByLayerId = {};
    733         this._scale = 0;
    734     },
    735 
    736     /**
    737      * @param {!WebGLRenderingContext} glContext
    738      */
    739     setContext: function(glContext)
    740     {
    741         this._gl = glContext;
    742         if (this._scale)
    743             this._updateTextures();
    744     },
    745 
    746     /**
    747      * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} paintTiles
    748      */
    749     setTiles: function(paintTiles)
    750     {
    751         this._tilesByLayerId = {};
    752         if (!paintTiles)
    753             return;
    754         for (var i = 0; i < paintTiles.length; ++i) {
    755             var layerId = paintTiles[i].layerId;
    756             var tilesForLayer = this._tilesByLayerId[layerId];
    757             if (!tilesForLayer) {
    758                 tilesForLayer = [];
    759                 this._tilesByLayerId[layerId] = tilesForLayer;
    760             }
    761             var tile = new WebInspector.LayerTextureManager.Tile(paintTiles[i].snapshot, paintTiles[i].rect, paintTiles[i].traceEvent);
    762             tilesForLayer.push(tile);
    763             if (this._scale && this._gl)
    764                 this._updateTile(tile);
    765         }
    766     },
    767 
    768     /**
    769      * @param {number} scale
    770      */
    771     setScale: function(scale)
    772     {
    773         if (this._scale && this._scale >= scale)
    774             return;
    775         this._scale = scale;
    776         this._updateTextures();
    777     },
    778 
    779     /**
    780      * @param {string} layerId
    781      * @return {!Array.<!WebInspector.LayerTextureManager.Tile>}
    782      */
    783     tilesForLayer: function(layerId)
    784     {
    785         return this._tilesByLayerId[layerId] || [];
    786     },
    787 
    788     _updateTextures: function()
    789     {
    790         if (!this._gl)
    791             return;
    792         if (!this._scale)
    793             return;
    794 
    795         for (var layerId in this._tilesByLayerId) {
    796             for (var i = 0; i < this._tilesByLayerId[layerId].length; ++i) {
    797                 var tile = this._tilesByLayerId[layerId][i];
    798                 if (!tile.scale || tile.scale < this._scale)
    799                     this._updateTile(tile);
    800             }
    801         }
    802     },
    803 
    804     /**
    805      * @param {!WebInspector.LayerTextureManager.Tile} tile
    806      */
    807     _updateTile: function(tile)
    808     {
    809         console.assert(this._scale && this._gl);
    810         tile.scale = this._scale;
    811         tile.snapshot.requestImage(null, null, tile.scale, onGotImage.bind(this));
    812 
    813         /**
    814          * @this {WebInspector.LayerTextureManager}
    815          * @param {string=} imageURL
    816          */
    817         function onGotImage(imageURL)
    818         {
    819             this.createTexture(onTextureCreated.bind(this), imageURL);
    820         }
    821 
    822         /**
    823          * @this {WebInspector.LayerTextureManager}
    824          * @param {!WebGLTexture} texture
    825          */
    826         function onTextureCreated(texture)
    827         {
    828             tile.texture = texture;
    829             this.dispatchEventToListeners(WebInspector.LayerTextureManager.Events.TextureUpdated);
    830         }
    831     },
    832 
    833     /**
    834      * @param {!function(!WebGLTexture)} textureCreatedCallback
    835      * @param {string=} imageURL
    836      */
    837     createTexture: function(textureCreatedCallback, imageURL)
    838     {
    839         var image = new Image();
    840         image.addEventListener("load", onImageLoaded.bind(this), false);
    841         image.src = imageURL;
    842 
    843         /**
    844          * @this {WebInspector.LayerTextureManager}
    845          */
    846         function onImageLoaded()
    847         {
    848             textureCreatedCallback(this._createTextureForImage(image));
    849         }
    850     },
    851 
    852     /**
    853      * @param {!Image} image
    854      * @return {!WebGLTexture} texture
    855      */
    856     _createTextureForImage: function(image)
    857     {
    858         var texture = this._gl.createTexture();
    859         texture.image = image;
    860         this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
    861         this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true);
    862         this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, texture.image);
    863         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR);
    864         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR);
    865         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE);
    866         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE);
    867         this._gl.bindTexture(this._gl.TEXTURE_2D, null);
    868         return texture;
    869     },
    870 
    871     __proto__: WebInspector.Object.prototype
    872 }
    873 
    874 /**
    875  * @constructor
    876  * @param {?WebInspector.Layers3DView.ActiveObject} relatedObject
    877  */
    878 WebInspector.Layers3DView.Rectangle = function(relatedObject)
    879 {
    880     this.relatedObject = relatedObject;
    881     /** @type {number} */
    882     this.lineWidth = 1;
    883     /** @type {?Array.<number>} */
    884     this.borderColor = null;
    885     /** @type {?Array.<number>} */
    886     this.fillColor = null;
    887     /** @type {?WebGLTexture} */
    888     this.texture = null;
    889 }
    890 
    891 WebInspector.Layers3DView.Rectangle.prototype = {
    892     /**
    893      * @param {!Array.<number>} quad
    894      * @param {number} z
    895      */
    896     setVertices: function(quad, z)
    897     {
    898         this.vertices = [quad[0], quad[1], z, quad[2], quad[3], z, quad[4], quad[5], z, quad[6], quad[7], z];
    899     },
    900 
    901     /**
    902      * Finds coordinates of point on layer quad, having offsets (ratioX * width) and (ratioY * height)
    903      * from the left corner of the initial layer rect, where width and heigth are layer bounds.
    904      * @param {!Array.<number>} quad
    905      * @param {number} ratioX
    906      * @param {number} ratioY
    907      * @return {!Array.<number>}
    908      */
    909     _calculatePointOnQuad: function(quad, ratioX, ratioY)
    910     {
    911         var x0 = quad[0];
    912         var y0 = quad[1];
    913         var x1 = quad[2];
    914         var y1 = quad[3];
    915         var x2 = quad[4];
    916         var y2 = quad[5];
    917         var x3 = quad[6];
    918         var y3 = quad[7];
    919         // Point on the first quad side clockwise
    920         var firstSidePointX = x0 + ratioX * (x1 - x0);
    921         var firstSidePointY = y0 + ratioX * (y1 - y0);
    922         // Point on the third quad side clockwise
    923         var thirdSidePointX = x3 + ratioX * (x2 - x3);
    924         var thirdSidePointY = y3 + ratioX * (y2 - y3);
    925         var x = firstSidePointX + ratioY * (thirdSidePointX - firstSidePointX);
    926         var y = firstSidePointY + ratioY * (thirdSidePointY - firstSidePointY);
    927         return [x, y];
    928     },
    929 
    930     /**
    931      * @param {!WebInspector.Layer} layer
    932      * @param {!DOMAgent.Rect} rect
    933      * @param {number} z
    934      */
    935     calculateVerticesFromRect: function(layer, rect, z)
    936     {
    937         var quad = layer.quad();
    938         var rx1 = rect.x / layer.width();
    939         var rx2 = (rect.x + rect.width) / layer.width();
    940         var ry1 = rect.y / layer.height();
    941         var ry2 = (rect.y + rect.height) / layer.height();
    942         var rectQuad = this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1))
    943             .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2));
    944         this.setVertices(rectQuad, z);
    945     },
    946 
    947     /**
    948      * Intersects quad with given transform matrix and line l(t) = (x0, y0, t)
    949      * @param {!CSSMatrix} matrix
    950      * @param {!number} x0
    951      * @param {!number} y0
    952      * @return {(number|undefined)}
    953      */
    954     intersectWithLine: function(matrix, x0, y0)
    955     {
    956         var epsilon = 1e-8;
    957         var i;
    958         // Vertices of the quad with transform matrix applied
    959         var points = [];
    960         for (i = 0; i < 4; ++i)
    961             points[i] = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(this.vertices[i * 3], this.vertices[i * 3 + 1], this.vertices[i * 3 + 2]), matrix);
    962         // Calculating quad plane normal
    963         var normal = WebInspector.Geometry.crossProduct(WebInspector.Geometry.subtract(points[1], points[0]), WebInspector.Geometry.subtract(points[2], points[1]));
    964         // General form of the equation of the quad plane: A * x + B * y + C * z + D = 0
    965         var A = normal.x;
    966         var B = normal.y;
    967         var C = normal.z;
    968         var D = -(A * points[0].x + B * points[0].y + C * points[0].z);
    969         // Finding t from the equation
    970         var t = -(D + A * x0 + B * y0) / C;
    971         // Point of the intersection
    972         var pt = new WebInspector.Geometry.Vector(x0, y0, t);
    973         // Vectors from the intersection point to vertices of the quad
    974         var tVects = points.map(WebInspector.Geometry.subtract.bind(null, pt));
    975         // Intersection point lies inside of the polygon if scalar products of normal of the plane and
    976         // cross products of successive tVects are all nonstrictly above or all nonstrictly below zero
    977         for (i = 0; i < tVects.length; ++i) {
    978             var product = WebInspector.Geometry.scalarProduct(normal, WebInspector.Geometry.crossProduct(tVects[i], tVects[(i + 1) % tVects.length]));
    979             if (product < 0)
    980                 return undefined;
    981         }
    982         return t;
    983     }
    984 }
    985 
    986 /**
    987  * @constructor
    988  */
    989 WebInspector.Layers3DView.ActiveObject = function()
    990 {
    991 }
    992 
    993 /**
    994  * @enum {string}
    995  */
    996 WebInspector.Layers3DView.ActiveObject.Type = {
    997     Layer: "Layer",
    998     ScrollRect: "ScrollRect",
    999     Tile: "Tile",
   1000 };
   1001 
   1002 /**
   1003  * @param {!WebInspector.Layer} layer
   1004  * @return {!WebInspector.Layers3DView.ActiveObject}
   1005  */
   1006 WebInspector.Layers3DView.ActiveObject.createLayerActiveObject = function(layer)
   1007 {
   1008     var activeObject = new WebInspector.Layers3DView.ActiveObject();
   1009     activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Layer;
   1010     activeObject.layer = layer;
   1011     return activeObject;
   1012 }
   1013 
   1014 /**
   1015  * @param {!WebInspector.Layer} layer
   1016  * @param {number} scrollRectIndex
   1017  * @return {!WebInspector.Layers3DView.ActiveObject}
   1018  */
   1019 WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject = function(layer, scrollRectIndex)
   1020 {
   1021     var activeObject = new WebInspector.Layers3DView.ActiveObject();
   1022     activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.ScrollRect;
   1023     activeObject.layer = layer;
   1024     activeObject.scrollRectIndex = scrollRectIndex;
   1025     return activeObject;
   1026 }
   1027 
   1028 /**
   1029  * @param {!WebInspector.Layer} layer
   1030  * @param {!WebInspector.TracingModel.Event} traceEvent
   1031  * @return {!WebInspector.Layers3DView.ActiveObject}
   1032  */
   1033 WebInspector.Layers3DView.ActiveObject.createTileActiveObject = function(layer, traceEvent)
   1034 {
   1035     var activeObject = new WebInspector.Layers3DView.ActiveObject();
   1036     activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Tile;
   1037     activeObject.layer = layer;
   1038     activeObject.traceEvent = traceEvent;
   1039     return activeObject;
   1040 }
   1041 
   1042 WebInspector.Layers3DView.ActiveObject.prototype = {
   1043     /**
   1044      * @return {!WebInspector.Layers3DView.ActiveObject.Type}
   1045      */
   1046     type: function()
   1047     {
   1048         return this._type;
   1049     }
   1050 };
   1051 
   1052 /**
   1053  * @constructor
   1054  * @param {!WebInspector.PaintProfilerSnapshot} snapshot
   1055  * @param {!Array.<number>} rect
   1056  * @param {!WebInspector.TracingModel.Event} traceEvent
   1057  */
   1058 WebInspector.LayerTextureManager.Tile = function(snapshot, rect, traceEvent)
   1059 {
   1060     this.snapshot = snapshot;
   1061     this.rect = rect;
   1062     this.traceEvent = traceEvent;
   1063     this.scale = 0;
   1064     /** @type {?WebGLTexture} */
   1065     this.texture = null;
   1066 }
   1067