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