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("Not in the composited mode.\nConsider forcing composited mode in Settings."));
     40     this._canvasElement = this.element.createChild("canvas");
     41     this._transformController = new WebInspector.TransformController(this._canvasElement);
     42     this._transformController.addEventListener(WebInspector.TransformController.Events.TransformChanged, this._update, this);
     43     this._canvasElement.addEventListener("dblclick", this._onDoubleClick.bind(this), false);
     44     this._canvasElement.addEventListener("mousedown", this._onMouseDown.bind(this), false);
     45     this._canvasElement.addEventListener("mouseup", this._onMouseUp.bind(this), false);
     46     this._canvasElement.addEventListener("mouseout", this._onMouseMove.bind(this), false);
     47     this._canvasElement.addEventListener("mousemove", this._onMouseMove.bind(this), false);
     48     this._canvasElement.addEventListener("contextmenu", this._onContextMenu.bind(this), false);
     49     this._lastActiveObject = {};
     50     this._picturesForLayer = {};
     51     this._scrollRectQuadsForLayer = {};
     52     this._isVisible = {};
     53     this._layerTree = null;
     54     WebInspector.settings.showPaintRects.addChangeListener(this._update, this);
     55 }
     56 
     57 /** @typedef {{layer: !WebInspector.Layer, scrollRectIndex: number}|{layer: !WebInspector.Layer}} */
     58 WebInspector.Layers3DView.ActiveObject;
     59 
     60 /** @typedef {{color: !Array.<number>, borderColor: !Array.<number>, borderWidth: number}} */
     61 WebInspector.Layers3DView.LayerStyle;
     62 
     63 /** @typedef {{layerId: string, rect: !Array.<number>, imageURL: string}} */
     64 WebInspector.Layers3DView.Tile;
     65 
     66 /**
     67  * @enum {string}
     68  */
     69 WebInspector.Layers3DView.OutlineType = {
     70     Hovered: "hovered",
     71     Selected: "selected"
     72 }
     73 
     74 /**
     75  * @enum {string}
     76  */
     77 WebInspector.Layers3DView.Events = {
     78     ObjectHovered: "ObjectHovered",
     79     ObjectSelected: "ObjectSelected",
     80     LayerSnapshotRequested: "LayerSnapshotRequested"
     81 }
     82 
     83 /**
     84  * @enum {string}
     85  */
     86 WebInspector.Layers3DView.ScrollRectTitles = {
     87     RepaintsOnScroll: WebInspector.UIString("repaints on scroll"),
     88     TouchEventHandler: WebInspector.UIString("touch event listener"),
     89     WheelEventHandler: WebInspector.UIString("mousewheel event listener")
     90 }
     91 
     92 WebInspector.Layers3DView.FragmentShader = "\
     93     precision mediump float;\
     94     varying vec4 vColor;\
     95     varying vec2 vTextureCoord;\
     96     uniform sampler2D uSampler;\
     97     void main(void)\
     98     {\
     99         gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)) * vColor;\
    100     }";
    101 
    102 WebInspector.Layers3DView.VertexShader = "\
    103     attribute vec3 aVertexPosition;\
    104     attribute vec2 aTextureCoord;\
    105     attribute vec4 aVertexColor;\
    106     uniform mat4 uPMatrix;\
    107     varying vec2 vTextureCoord;\
    108     varying vec4 vColor;\
    109     void main(void)\
    110     {\
    111         gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);\
    112         vColor = aVertexColor;\
    113         vTextureCoord = aTextureCoord;\
    114     }";
    115 
    116 WebInspector.Layers3DView.SelectedBackgroundColor = [20, 40, 110, 0.66];
    117 WebInspector.Layers3DView.BackgroundColor = [0, 0, 0, 0];
    118 WebInspector.Layers3DView.HoveredBorderColor = [0, 0, 255, 1];
    119 WebInspector.Layers3DView.SelectedBorderColor = [0, 255, 0, 1];
    120 WebInspector.Layers3DView.BorderColor = [0, 0, 0, 1];
    121 WebInspector.Layers3DView.ScrollRectBackgroundColor = [178, 0, 0, 0.4];
    122 WebInspector.Layers3DView.SelectedScrollRectBackgroundColor = [178, 0, 0, 0.6];
    123 WebInspector.Layers3DView.ScrollRectBorderColor = [178, 0, 0, 1];
    124 WebInspector.Layers3DView.BorderWidth = 1;
    125 WebInspector.Layers3DView.SelectedBorderWidth = 2;
    126 
    127 WebInspector.Layers3DView.LayerSpacing = 20;
    128 WebInspector.Layers3DView.ScrollRectSpacing = 4;
    129 
    130 WebInspector.Layers3DView.prototype = {
    131     /**
    132      * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(?Event=))} registerShortcutDelegate
    133      */
    134     registerShortcuts: function(registerShortcutDelegate)
    135     {
    136         this._transformController.registerShortcuts(registerShortcutDelegate);
    137     },
    138 
    139     onResize: function()
    140     {
    141         this._update();
    142     },
    143 
    144     willHide: function()
    145     {
    146     },
    147 
    148     wasShown: function()
    149     {
    150         if (this._needsUpdate)
    151             this._update();
    152     },
    153 
    154     /**
    155      * @param {!WebInspector.Layers3DView.OutlineType} type
    156      * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
    157      */
    158     _setOutline: function(type, activeObject)
    159     {
    160         this._lastActiveObject[type] = activeObject;
    161         this._update();
    162     },
    163 
    164     /**
    165      * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
    166      */
    167     hoverObject: function(activeObject)
    168     {
    169         this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, activeObject);
    170     },
    171 
    172     /**
    173      * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
    174      */
    175     selectObject: function(activeObject)
    176     {
    177         this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, null);
    178         this._setOutline(WebInspector.Layers3DView.OutlineType.Selected, activeObject);
    179     },
    180 
    181     /**
    182      * @param {!WebInspector.Layer} layer
    183      * @param {string=} imageURL
    184      */
    185     showImageForLayer: function(layer, imageURL)
    186     {
    187         this.setTiles([{layerId: layer.id(), rect: [0, 0, layer.width(), layer.height()], imageURL: imageURL}])
    188     },
    189 
    190     /**
    191      * @param {!Array.<!WebInspector.Layers3DView.Tile>} tiles
    192      */
    193     setTiles: function(tiles)
    194     {
    195         this._picturesForLayer = {};
    196         tiles.forEach(this._setTile, this);
    197     },
    198 
    199     /**
    200      * @param {!WebInspector.Layers3DView.Tile} tile
    201      */
    202     _setTile: function(tile)
    203     {
    204         var texture = this._gl.createTexture();
    205         texture.image = new Image();
    206         texture.image.addEventListener("load", this._handleLoadedTexture.bind(this, texture, tile.layerId, tile.rect), false);
    207         texture.image.src = tile.imageURL;
    208     },
    209 
    210     /**
    211      * @param {!Element} canvas
    212      * @return {!Object}
    213      */
    214     _initGL: function(canvas)
    215     {
    216         var gl = canvas.getContext("webgl");
    217         gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    218         gl.enable(gl.BLEND);
    219         gl.clearColor(0.0, 0.0, 0.0, 0.0);
    220         gl.enable(gl.DEPTH_TEST);
    221         return gl;
    222     },
    223 
    224     /**
    225      * @param {!Object} type
    226      * @param {string} script
    227      */
    228     _createShader: function(type, script)
    229     {
    230         var shader = this._gl.createShader(type);
    231         this._gl.shaderSource(shader, script);
    232         this._gl.compileShader(shader);
    233         this._gl.attachShader(this._shaderProgram, shader);
    234     },
    235 
    236     /**
    237      * @param {string} attributeName
    238      * @param {string} glName
    239      */
    240     _enableVertexAttribArray: function(attributeName, glName)
    241     {
    242         this._shaderProgram[attributeName] = this._gl.getAttribLocation(this._shaderProgram, glName);
    243         this._gl.enableVertexAttribArray(this._shaderProgram[attributeName]);
    244     },
    245 
    246     _initShaders: function()
    247     {
    248         this._shaderProgram = this._gl.createProgram();
    249         this._createShader(this._gl.FRAGMENT_SHADER, WebInspector.Layers3DView.FragmentShader);
    250         this._createShader(this._gl.VERTEX_SHADER, WebInspector.Layers3DView.VertexShader);
    251         this._gl.linkProgram(this._shaderProgram);
    252         this._gl.useProgram(this._shaderProgram);
    253 
    254         this._shaderProgram.vertexPositionAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexPosition");
    255         this._gl.enableVertexAttribArray(this._shaderProgram.vertexPositionAttribute);
    256         this._shaderProgram.vertexColorAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexColor");
    257         this._gl.enableVertexAttribArray(this._shaderProgram.vertexColorAttribute);
    258         this._shaderProgram.textureCoordAttribute = this._gl.getAttribLocation(this._shaderProgram, "aTextureCoord");
    259         this._gl.enableVertexAttribArray(this._shaderProgram.textureCoordAttribute);
    260 
    261         this._shaderProgram.pMatrixUniform = this._gl.getUniformLocation(this._shaderProgram, "uPMatrix");
    262         this._shaderProgram.samplerUniform = this._gl.getUniformLocation(this._shaderProgram, "uSampler");
    263     },
    264 
    265     _resizeCanvas: function()
    266     {
    267         this._canvasElement.width = this._canvasElement.offsetWidth * window.devicePixelRatio;
    268         this._canvasElement.height = this._canvasElement.offsetHeight * window.devicePixelRatio;
    269         this._gl.viewportWidth = this._canvasElement.width;
    270         this._gl.viewportHeight = this._canvasElement.height;
    271     },
    272 
    273     /**
    274      * @return {!CSSMatrix}
    275      */
    276     _calculateProjectionMatrix: function()
    277     {
    278         var scaleFactorForMargins = 1.2;
    279         var viewport = this._layerTree.viewportSize();
    280         var baseWidth = viewport ? viewport.width : this._layerTree.contentRoot().width();
    281         var baseHeight = viewport ? viewport.height : this._layerTree.contentRoot().height();
    282         var canvasWidth = this._canvasElement.width;
    283         var canvasHeight = this._canvasElement.height;
    284         var scaleX = canvasWidth / baseWidth / scaleFactorForMargins;
    285         var scaleY = canvasHeight / baseHeight / scaleFactorForMargins;
    286         var viewScale = Math.min(scaleX, scaleY);
    287         var scale = this._transformController.scale();
    288         var offsetX = this._transformController.offsetX() * window.devicePixelRatio;
    289         var offsetY = this._transformController.offsetY() * window.devicePixelRatio;
    290         var rotateX = this._transformController.rotateX();
    291         var rotateY = this._transformController.rotateY();
    292         return new WebKitCSSMatrix().translate(offsetX, offsetY, 0).scale(scale, scale, scale).translate(canvasWidth / 2, canvasHeight / 2, 0)
    293             .rotate(rotateX, rotateY, 0).scale(viewScale, viewScale, viewScale).translate(-baseWidth / 2, -baseHeight / 2, 0);
    294     },
    295 
    296     _initProjectionMatrix: function()
    297     {
    298         this._pMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0)
    299             .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(this._calculateProjectionMatrix());
    300         this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(this._pMatrix));
    301     },
    302 
    303     /**
    304      * @param {!Object} texture
    305      * @param {string} layerId
    306      * @param {!Array.<number>} rect
    307      */
    308     _handleLoadedTexture: function(texture, layerId, rect)
    309     {
    310         this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
    311         this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true);
    312         this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, texture.image);
    313         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR);
    314         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR);
    315         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE);
    316         this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE);
    317         this._gl.bindTexture(this._gl.TEXTURE_2D, null);
    318         if (!this._picturesForLayer[layerId])
    319             this._picturesForLayer[layerId] = [];
    320         this._picturesForLayer[layerId].push({texture: texture, rect: rect});
    321         this._update();
    322     },
    323 
    324     _initWhiteTexture: function()
    325     {
    326         this._whiteTexture = this._gl.createTexture();
    327         this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture);
    328         var whitePixel = new Uint8Array([255, 255, 255, 255]);
    329         this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, 1, 1, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, whitePixel);
    330     },
    331 
    332     _initGLIfNecessary: function()
    333     {
    334         if (this._gl)
    335             return this._gl;
    336         this._gl = this._initGL(this._canvasElement);
    337         this._initShaders();
    338         this._initWhiteTexture();
    339         return this._gl;
    340     },
    341 
    342     /**
    343      * @param {!CSSMatrix} m
    344      * @return {!Float32Array}
    345      */
    346     _arrayFromMatrix: function(m)
    347     {
    348         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]);
    349     },
    350 
    351     /**
    352      * @param {!Array.<number>} color
    353      * @return {!Array.<number>}
    354      */
    355     _makeColorsArray: function(color)
    356     {
    357         var colors = [];
    358         var normalizedColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3]];
    359         for (var i = 0; i < 4; i++) {
    360             colors = colors.concat(normalizedColor);
    361         }
    362         return colors;
    363     },
    364 
    365     /**
    366      * @param {!Object} attribute
    367      * @param {!Array.<number>} array
    368      * @param {!number} length
    369      */
    370     _setVertexAttribute: function(attribute, array, length)
    371     {
    372         var gl = this._gl;
    373         var buffer = gl.createBuffer();
    374         gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    375         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW);
    376         gl.vertexAttribPointer(attribute, length, gl.FLOAT, false, 0, 0);
    377     },
    378 
    379     /**
    380      * @param {!Array.<number>} vertices
    381      * @param {!Array.<number>} color
    382      * @param {!Object} glMode
    383      * @param {!Object=} texture
    384      */
    385     _drawRectangle: function(vertices, color, glMode, texture)
    386     {
    387         this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3);
    388         this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2);
    389 
    390         if (texture) {
    391             var white = [255, 255, 255, 1];
    392             this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length);
    393             this._gl.activeTexture(this._gl.TEXTURE0);
    394             this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
    395             this._gl.uniform1i(this._shaderProgram.samplerUniform, 0);
    396         } else {
    397             this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color), color.length);
    398             this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture);
    399         }
    400 
    401         var numberOfVertices = 4;
    402         this._gl.drawArrays(glMode, 0, numberOfVertices);
    403     },
    404 
    405     /**
    406      * @param {!WebInspector.Layers3DView.OutlineType} type
    407      * @param {!WebInspector.Layer} layer
    408      * @param {number=} scrollRectIndex
    409      */
    410     _isObjectActive: function(type, layer, scrollRectIndex)
    411     {
    412         var activeObject = this._lastActiveObject[type];
    413         return activeObject && activeObject.layer && activeObject.layer.id() === layer.id() && (typeof scrollRectIndex !== "number" || activeObject.scrollRectIndex === scrollRectIndex);
    414     },
    415 
    416     /**
    417      * @param {!WebInspector.Layer} layer
    418      * @return {!WebInspector.Layers3DView.LayerStyle}
    419      */
    420     _styleForLayer: function(layer)
    421     {
    422         var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer);
    423         var isHovered = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Hovered, layer);
    424         var color = isSelected ? WebInspector.Layers3DView.SelectedBackgroundColor : WebInspector.Layers3DView.BackgroundColor;
    425         var borderColor;
    426         if (isSelected)
    427             borderColor = WebInspector.Layers3DView.SelectedBorderColor;
    428         else if (isHovered)
    429             borderColor = WebInspector.Layers3DView.HoveredBorderColor;
    430         else
    431             borderColor = WebInspector.Layers3DView.BorderColor;
    432         var borderWidth = isSelected ? WebInspector.Layers3DView.SelectedBorderWidth : WebInspector.Layers3DView.BorderWidth;
    433         return {color: color, borderColor: borderColor, borderWidth: borderWidth};
    434     },
    435 
    436     /**
    437      * @param {!Array.<number>} quad
    438      * @param {number} z
    439      * @return {!Array.<number>}
    440      */
    441     _calculateVerticesForQuad: function(quad, z)
    442     {
    443         return [quad[0], quad[1], z, quad[2], quad[3], z, quad[4], quad[5], z, quad[6], quad[7], z];
    444     },
    445 
    446     /**
    447      * Finds coordinates of point on layer quad, having offsets (ratioX * width) and (ratioY * height)
    448      * from the left corner of the initial layer rect, where width and heigth are layer bounds.
    449      * @param {!Array.<number>} quad
    450      * @param {number} ratioX
    451      * @param {number} ratioY
    452      * @return {!Array.<number>}
    453      */
    454     _calculatePointOnQuad: function(quad, ratioX, ratioY)
    455     {
    456         var x0 = quad[0];
    457         var y0 = quad[1];
    458         var x1 = quad[2];
    459         var y1 = quad[3];
    460         var x2 = quad[4];
    461         var y2 = quad[5];
    462         var x3 = quad[6];
    463         var y3 = quad[7];
    464         // Point on the first quad side clockwise
    465         var firstSidePointX = x0 + ratioX * (x1 - x0);
    466         var firstSidePointY = y0 + ratioX * (y1 - y0);
    467         // Point on the third quad side clockwise
    468         var thirdSidePointX = x3 + ratioX * (x2 - x3);
    469         var thirdSidePointY = y3 + ratioX * (y2 - y3);
    470         var x = firstSidePointX + ratioY * (thirdSidePointX - firstSidePointX);
    471         var y = firstSidePointY + ratioY * (thirdSidePointY - firstSidePointY);
    472         return [x, y];
    473     },
    474 
    475     /**
    476      * @param {!WebInspector.Layer} layer
    477      * @param {!DOMAgent.Rect} rect
    478      * @return {!Array.<number>}
    479      */
    480     _calculateRectQuad: function(layer, rect)
    481     {
    482         var quad = layer.quad();
    483         var rx1 = rect.x / layer.width();
    484         var rx2 = (rect.x + rect.width) / layer.width();
    485         var ry1 = rect.y / layer.height();
    486         var ry2 = (rect.y + rect.height) / layer.height();
    487         return this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1))
    488             .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2));
    489     },
    490 
    491     /**
    492      * @param {!WebInspector.Layer} layer
    493      * @return {!Array.<!Array.<number>>}
    494      */
    495     _calculateScrollRectQuadsForLayer: function(layer)
    496     {
    497         var quads = [];
    498         for (var i = 0; i < layer.scrollRects().length; ++i)
    499             quads.push(this._calculateRectQuad(layer, layer.scrollRects()[i].rect));
    500         return quads;
    501     },
    502 
    503     /**
    504      * @param {!WebInspector.Layer} layer
    505      * @param {number} index
    506      * @return {number}
    507      */
    508     _calculateScrollRectDepth: function(layer, index)
    509     {
    510         return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing + index * WebInspector.Layers3DView.ScrollRectSpacing + 1;
    511     },
    512 
    513     /**
    514      * @param {!WebInspector.Layer} layer
    515      */
    516     _drawLayer: function(layer)
    517     {
    518         var gl = this._gl;
    519         var vertices;
    520         var style = this._styleForLayer(layer);
    521         var layerDepth = this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing;
    522         if (this._isVisible[layer.id()]) {
    523             vertices = this._calculateVerticesForQuad(layer.quad(), layerDepth);
    524             gl.lineWidth(style.borderWidth);
    525             this._drawRectangle(vertices, style.borderColor, gl.LINE_LOOP);
    526             gl.lineWidth(1);
    527         }
    528         this._scrollRectQuadsForLayer[layer.id()] = this._calculateScrollRectQuadsForLayer(layer);
    529         var scrollRectQuads = this._scrollRectQuadsForLayer[layer.id()];
    530         for (var i = 0; i < scrollRectQuads.length; ++i) {
    531             vertices = this._calculateVerticesForQuad(scrollRectQuads[i], this._calculateScrollRectDepth(layer, i));
    532             var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i);
    533             var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor;
    534             this._drawRectangle(vertices, color, gl.TRIANGLE_FAN);
    535             this._drawRectangle(vertices, WebInspector.Layers3DView.ScrollRectBorderColor, gl.LINE_LOOP);
    536         }
    537         var tiles = this._picturesForLayer[layer.id()] || [];
    538         for (var i = 0; i < tiles.length; ++i) {
    539             var tile = tiles[i];
    540             var quad = this._calculateRectQuad(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2] - tile.rect[0], height: tile.rect[3] - tile.rect[1]});
    541             vertices = this._calculateVerticesForQuad(quad, layerDepth);
    542             this._drawRectangle(vertices, style.color, gl.TRIANGLE_FAN, tile.texture);
    543         }
    544     },
    545 
    546     _drawViewport: function()
    547     {
    548         var viewport = this._layerTree.viewportSize();
    549         var vertices = [0, 0, 0, viewport.width, 0, 0, viewport.width, viewport.height, 0, 0, viewport.height, 0];
    550         var color = [0, 0, 0, 1];
    551         this._gl.lineWidth(3.0);
    552         this._drawRectangle(vertices, color, this._gl.LINE_LOOP);
    553         this._gl.lineWidth(1.0);
    554     },
    555 
    556     _calculateDepths: function()
    557     {
    558         this._depthByLayerId = {};
    559         this._isVisible = {};
    560         var depth = 0;
    561         var root = this._layerTree.root();
    562         var queue = [root];
    563         this._depthByLayerId[root.id()] = 0;
    564         this._isVisible[root.id()] = false;
    565         while (queue.length > 0) {
    566             var layer = queue.shift();
    567             var children = layer.children();
    568             for (var i = 0; i < children.length; ++i) {
    569                 this._depthByLayerId[children[i].id()] = ++depth;
    570                 this._isVisible[children[i].id()] = children[i] === this._layerTree.contentRoot() || this._isVisible[layer.id()];
    571                 queue.push(children[i]);
    572             }
    573         }
    574     },
    575 
    576 
    577     /**
    578      * @param {?WebInspector.LayerTreeBase} layerTree
    579      */
    580     setLayerTree: function(layerTree)
    581     {
    582         this._layerTree = layerTree;
    583         this._update();
    584     },
    585 
    586     _update: function()
    587     {
    588         if (!this.isShowing()) {
    589             this._needsUpdate = true;
    590             return;
    591         }
    592         var contentRoot = this._layerTree && this._layerTree.contentRoot();
    593         if (!contentRoot || !this._layerTree.root()) {
    594             this._emptyView.show(this.element);
    595             return;
    596         }
    597         this._emptyView.detach();
    598 
    599         var gl = this._initGLIfNecessary();
    600         this._resizeCanvas();
    601         this._initProjectionMatrix();
    602         this._calculateDepths();
    603 
    604         gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    605         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    606 
    607         if (this._layerTree.viewportSize())
    608             this._drawViewport();
    609         this._layerTree.forEachLayer(this._drawLayer.bind(this));
    610     },
    611 
    612     /**
    613      * Intersects quad with given transform matrix and line l(t) = (x0, y0, t)
    614      * @param {!Array.<number>} vertices
    615      * @param {!CSSMatrix} matrix
    616      * @param {!number} x0
    617      * @param {!number} y0
    618      * @return {(number|undefined)}
    619      */
    620     _intersectLineAndRect: function(vertices, matrix, x0, y0)
    621     {
    622         var epsilon = 1e-8;
    623         var i;
    624         // Vertices of the quad with transform matrix applied
    625         var points = [];
    626         for (i = 0; i < 4; ++i)
    627             points[i] = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]), matrix);
    628         // Calculating quad plane normal
    629         var normal = WebInspector.Geometry.crossProduct(WebInspector.Geometry.subtract(points[1], points[0]), WebInspector.Geometry.subtract(points[2], points[1]));
    630         // General form of the equation of the quad plane: A * x + B * y + C * z + D = 0
    631         var A = normal.x;
    632         var B = normal.y;
    633         var C = normal.z;
    634         var D = -(A * points[0].x + B * points[0].y + C * points[0].z);
    635         // Finding t from the equation
    636         var t = -(D + A * x0 + B * y0) / C;
    637         // Point of the intersection
    638         var pt = new WebInspector.Geometry.Vector(x0, y0, t);
    639         // Vectors from the intersection point to vertices of the quad
    640         var tVects = points.map(WebInspector.Geometry.subtract.bind(null, pt));
    641         // Intersection point lies inside of the polygon if scalar products of normal of the plane and
    642         // cross products of successive tVects are all nonstrictly above or all nonstrictly below zero
    643         for (i = 0; i < tVects.length; ++i) {
    644             var product = WebInspector.Geometry.scalarProduct(normal, WebInspector.Geometry.crossProduct(tVects[i], tVects[(i + 1) % tVects.length]));
    645             if (product < 0)
    646                 return undefined;
    647         }
    648         return t;
    649     },
    650 
    651     /**
    652      * @param {?Event} event
    653      * @return {?WebInspector.Layers3DView.ActiveObject}
    654      */
    655     _activeObjectFromEventPoint: function(event)
    656     {
    657         if (!this._layerTree)
    658             return null;
    659         var closestIntersectionPoint = Infinity;
    660         var closestLayer = null;
    661         var projectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0).multiply(this._calculateProjectionMatrix());
    662         var x0 = (event.clientX - this._canvasElement.totalOffsetLeft()) * window.devicePixelRatio;
    663         var y0 = -(event.clientY - this._canvasElement.totalOffsetTop()) * window.devicePixelRatio;
    664 
    665         /**
    666          * @param {!WebInspector.Layer} layer
    667          * @this {WebInspector.Layers3DView}
    668          */
    669         function checkIntersection(layer)
    670         {
    671             var t;
    672             if (this._isVisible[layer.id()]) {
    673                 t = this._intersectLineAndRect(this._calculateVerticesForQuad(layer.quad(), this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing), projectionMatrix, x0, y0);
    674                 if (t < closestIntersectionPoint) {
    675                     closestIntersectionPoint = t;
    676                     closestLayer = {layer: layer};
    677                 }
    678             }
    679             var scrollRectQuads = this._scrollRectQuadsForLayer[layer.id()];
    680             for (var i = 0; i < scrollRectQuads.length; ++i) {
    681                 t = this._intersectLineAndRect(this._calculateVerticesForQuad(scrollRectQuads[i], this._calculateScrollRectDepth(layer, i)), projectionMatrix, x0, y0);
    682                 if (t < closestIntersectionPoint) {
    683                     closestIntersectionPoint = t;
    684                     closestLayer = {layer: layer, scrollRectIndex: i};
    685                 }
    686             }
    687         }
    688 
    689         this._layerTree.forEachLayer(checkIntersection.bind(this));
    690         return closestLayer;
    691     },
    692 
    693     /**
    694      * @param {?Event} event
    695      */
    696     _onContextMenu: function(event)
    697     {
    698         var activeObject = this._activeObjectFromEventPoint(event);
    699         var node = activeObject && activeObject.layer && activeObject.layer.nodeForSelfOrAncestor();
    700         var contextMenu = new WebInspector.ContextMenu(event);
    701         contextMenu.appendItem("Reset view", this._transformController._resetAndNotify.bind(this._transformController), false);
    702         if (node)
    703             contextMenu.appendApplicableItems(node);
    704         contextMenu.show();
    705     },
    706 
    707     /**
    708      * @param {?Event} event
    709      */
    710     _onMouseMove: function(event)
    711     {
    712         if (event.which)
    713             return;
    714         this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectHovered, this._activeObjectFromEventPoint(event));
    715     },
    716 
    717     /**
    718      * @param {?Event} event
    719      */
    720     _onMouseDown: function(event)
    721     {
    722         this._mouseDownX = event.clientX;
    723         this._mouseDownY = event.clientY;
    724     },
    725 
    726     /**
    727      * @param {?Event} event
    728      */
    729     _onMouseUp: function(event)
    730     {
    731         const maxDistanceInPixels = 6;
    732         if (this._mouseDownX && Math.abs(event.clientX - this._mouseDownX) < maxDistanceInPixels && Math.abs(event.clientY - this._mouseDownY) < maxDistanceInPixels)
    733             this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectSelected, this._activeObjectFromEventPoint(event));
    734         delete this._mouseDownX;
    735         delete this._mouseDownY;
    736     },
    737 
    738     /**
    739      * @param {?Event} event
    740      */
    741     _onDoubleClick: function(event)
    742     {
    743         var object = this._activeObjectFromEventPoint(event);
    744         if (object && object.layer)
    745             this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer);
    746         event.stopPropagation();
    747     },
    748 
    749     __proto__: WebInspector.VBox.prototype
    750 }
    751