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