Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / ResponsiveDesignView.js
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  * @constructor
7  * @extends {WebInspector.VBox}
8  * @implements {WebInspector.OverridesSupport.PageResizer}
9  * @param {!WebInspector.InspectedPagePlaceholder} inspectedPagePlaceholder
10  */
11 WebInspector.ResponsiveDesignView = function(inspectedPagePlaceholder)
12 {
13     WebInspector.VBox.call(this);
14     this.setMinimumSize(150, 150);
15     this.registerRequiredCSS("responsiveDesignView.css");
16     this.element.classList.add("overflow-hidden");
17
18     this._responsiveDesignContainer = new WebInspector.VBox();
19
20     this._createToolbar();
21
22     this._mediaInspector = new WebInspector.MediaQueryInspector();
23     this._mediaInspectorContainer = this._responsiveDesignContainer.element.createChild("div");
24     this._updateMediaQueryInspector();
25
26     this._canvasContainer = new WebInspector.View();
27     this._canvasContainer.element.classList.add("responsive-design");
28     this._canvasContainer.show(this._responsiveDesignContainer.element);
29
30     this._canvas = this._canvasContainer.element.createChild("canvas", "fill");
31
32     this._rulerGlasspane = this._canvasContainer.element.createChild("div", "responsive-design-ruler-glasspane");
33     this._rulerGlasspane.appendChild(this._mediaInspector.rulerDecorationLayer());
34
35     this._warningMessage = this._canvasContainer.element.createChild("div", "responsive-design-warning hidden");
36     this._warningMessage.createChild("div", "warning-icon-small");
37     this._warningMessage.createChild("span");
38     var warningCloseButton = this._warningMessage.createChild("div", "close-button");
39     warningCloseButton.addEventListener("click", WebInspector.overridesSupport.clearWarningMessage.bind(WebInspector.overridesSupport), false);
40     WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.OverridesWarningUpdated, this._overridesWarningUpdated, this);
41
42     this._slidersContainer = this._canvasContainer.element.createChild("div", "vbox responsive-design-sliders-container");
43     var hbox = this._slidersContainer.createChild("div", "hbox flex-auto");
44     this._heightSliderContainer = this._slidersContainer.createChild("div", "hbox responsive-design-slider-height");
45     this._pageContainer = hbox.createChild("div", "vbox flex-auto");
46     this._widthSliderContainer = hbox.createChild("div", "vbox responsive-design-slider-width");
47
48     this._widthSlider = this._widthSliderContainer.createChild("div", "responsive-design-slider-thumb");
49     this._widthSlider.createChild("div", "responsive-design-thumb-handle");
50     this._createResizer(this._widthSlider, false);
51     this._heightSlider = this._heightSliderContainer.createChild("div", "responsive-design-slider-thumb");
52     this._heightSlider.createChild("div", "responsive-design-thumb-handle");
53     this._createResizer(this._heightSlider, true);
54
55     this._inspectedPagePlaceholder = inspectedPagePlaceholder;
56     inspectedPagePlaceholder.show(this.element);
57
58     this._enabled = false;
59
60     WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
61     WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.EmulationStateChanged, this._emulationEnabledChanged, this);
62     this._mediaInspector.addEventListener(WebInspector.MediaQueryInspector.Events.HeightUpdated, this.onResize, this);
63     this._emulationEnabledChanged();
64     this._overridesWarningUpdated();
65 };
66
67 // Measured in DIP.
68 WebInspector.ResponsiveDesignView.SliderWidth = 19;
69 WebInspector.ResponsiveDesignView.RulerWidth = 22;
70
71 WebInspector.ResponsiveDesignView.prototype = {
72     _invalidateCache: function()
73     {
74         delete this._cachedScale;
75         delete this._cachedCssCanvasWidth;
76         delete this._cachedCssCanvasHeight;
77         delete this._cachedCssHeight;
78         delete this._cachedCssWidth;
79         delete this._cachedZoomFactor;
80         delete this._availableSize;
81     },
82
83     _emulationEnabledChanged: function()
84     {
85         var enabled = WebInspector.overridesSupport.emulationEnabled();
86         this._mediaInspector.setEnabled(enabled);
87         if (enabled && !this._enabled) {
88             this._invalidateCache();
89             this._ignoreResize = true;
90             this._enabled = true;
91             this._inspectedPagePlaceholder.clearMinimumSizeAndMargins();
92             this._inspectedPagePlaceholder.show(this._pageContainer);
93             this._responsiveDesignContainer.show(this.element);
94             delete this._ignoreResize;
95             this.onResize();
96         } else if (!enabled && this._enabled) {
97             this._invalidateCache();
98             this._ignoreResize = true;
99             this._enabled = false;
100             this._scale = 1;
101             this._inspectedPagePlaceholder.restoreMinimumSizeAndMargins();
102             this._responsiveDesignContainer.detach();
103             this._inspectedPagePlaceholder.show(this.element);
104             delete this._ignoreResize;
105             this.onResize();
106         }
107     },
108
109     /**
110      * WebInspector.OverridesSupport.PageResizer override.
111      * @param {number} dipWidth
112      * @param {number} dipHeight
113      * @param {number} scale
114      */
115     update: function(dipWidth, dipHeight, scale)
116     {
117         this._scale = scale;
118         this._dipWidth = dipWidth;
119         this._dipHeight = dipHeight;
120         this._updateUI();
121     },
122
123     updatePageResizer: function()
124     {
125         WebInspector.overridesSupport.setPageResizer(this, this._availableDipSize());
126     },
127
128     /**
129      * @return {!Size}
130      */
131     _availableDipSize: function()
132     {
133         if (typeof this._availableSize === "undefined") {
134             var zoomFactor = WebInspector.zoomManager.zoomFactor();
135             var rect = this._canvasContainer.element.getBoundingClientRect();
136             this._availableSize = new Size(rect.width * zoomFactor - WebInspector.ResponsiveDesignView.RulerWidth,
137                                            rect.height * zoomFactor - WebInspector.ResponsiveDesignView.RulerWidth);
138         }
139         return this._availableSize;
140     },
141
142     /**
143      * @param {!Element} element
144      * @param {boolean} vertical
145      * @return {!WebInspector.ResizerWidget}
146      */
147     _createResizer: function(element, vertical)
148     {
149         var resizer = new WebInspector.ResizerWidget();
150         resizer.addElement(element);
151         resizer.setVertical(vertical);
152         resizer.addEventListener(WebInspector.ResizerWidget.Events.ResizeStart, this._onResizeStart, this);
153         resizer.addEventListener(WebInspector.ResizerWidget.Events.ResizeUpdate, this._onResizeUpdate, this);
154         resizer.addEventListener(WebInspector.ResizerWidget.Events.ResizeEnd, this._onResizeEnd, this);
155         return resizer;
156     },
157
158     /**
159      * @param {!WebInspector.Event} event
160      */
161     _onResizeStart: function(event)
162     {
163         var available = this._availableDipSize();
164         this._slowPositionStart = null;
165         this._resizeStartSize = event.target.isVertical() ? (this._dipHeight || available.height) : (this._dipWidth || available.width);
166     },
167
168     /**
169      * @param {!WebInspector.Event} event
170      */
171     _onResizeUpdate: function(event)
172     {
173         if (event.data.shiftKey !== !!this._slowPositionStart)
174             this._slowPositionStart = event.data.shiftKey ? event.data.currentPosition : null;
175         var cssOffset = this._slowPositionStart ? (event.data.currentPosition - this._slowPositionStart) / 10 + this._slowPositionStart - event.data.startPosition : event.data.currentPosition - event.data.startPosition;
176         var dipOffset = Math.round(cssOffset * WebInspector.zoomManager.zoomFactor());
177         var newSize = Math.max(this._resizeStartSize + dipOffset, 1);
178         var requested = {};
179         if (event.target.isVertical())
180             requested.height = newSize;
181         else
182             requested.width = newSize;
183         this.dispatchEventToListeners(WebInspector.OverridesSupport.PageResizer.Events.ResizeRequested, requested);
184     },
185
186     /**
187      * @param {!WebInspector.Event} event
188      */
189     _onResizeEnd: function(event)
190     {
191         delete this._resizeStartSize;
192     },
193
194     /**
195      * Draws canvas of the specified css size in DevTools page space.
196      * Canvas contains grid and rulers.
197      * @param {number} cssCanvasWidth
198      * @param {number} cssCanvasHeight
199      */
200     _drawCanvas: function(cssCanvasWidth, cssCanvasHeight)
201     {
202         if (!this._enabled)
203             return;
204
205         var canvas = this._canvas;
206         var context = canvas.getContext("2d");
207         canvas.style.width = cssCanvasWidth + "px";
208         canvas.style.height = cssCanvasHeight + "px";
209
210         var zoomFactor = WebInspector.zoomManager.zoomFactor();
211         var dipCanvasWidth = cssCanvasWidth * zoomFactor;
212         var dipCanvasHeight = cssCanvasHeight * zoomFactor;
213
214         var deviceScaleFactor = window.devicePixelRatio;
215         canvas.width = deviceScaleFactor * cssCanvasWidth;
216         canvas.height = deviceScaleFactor * cssCanvasHeight;
217         context.scale(canvas.width / dipCanvasWidth, canvas.height / dipCanvasHeight);
218         context.font = "11px " + WebInspector.fontFamily();
219
220         const rulerStep = 100;
221         const rulerSubStep = 5;
222         const gridStep = 50;
223         const gridSubStep = 10;
224         const rulerBackgroundColor = "rgb(0, 0, 0)";
225         const backgroundColor = "rgb(102, 102, 102)";
226         const lightLineColor = "rgb(132, 132, 132)";
227         const darkLineColor = "rgb(114, 114, 114)";
228         const rulerColor = "rgb(125, 125, 125)";
229         const textColor = "rgb(186, 186, 186)";
230
231         var scale = this._scale || 1;
232         var rulerWidth = WebInspector.ResponsiveDesignView.RulerWidth;
233         var dipGridWidth = dipCanvasWidth / scale - rulerWidth;
234         var dipGridHeight = dipCanvasHeight / scale - rulerWidth;
235         rulerWidth /= scale;
236         context.scale(scale, scale);
237         context.translate(rulerWidth, rulerWidth);
238
239         context.fillStyle = rulerBackgroundColor;
240         context.fillRect(-rulerWidth, -rulerWidth, dipGridWidth + rulerWidth, rulerWidth);
241         context.fillRect(-rulerWidth, 0, rulerWidth, dipGridHeight);
242
243         context.fillStyle = backgroundColor;
244         context.fillRect(0, 0, dipGridWidth, dipGridHeight);
245
246         context.translate(0.5, 0.5);
247         context.strokeStyle = rulerColor;
248         context.fillStyle = textColor;
249         context.lineWidth = 1;
250
251         // Draw vertical ruler.
252         for (var x = 0; x < dipGridWidth; x += rulerSubStep) {
253             var y = -rulerWidth / 4;
254             if (!(x % (rulerStep / 4)))
255                 y = -rulerWidth / 2;
256             if (!(x % (rulerStep / 2)))
257                 y = -rulerWidth + 2;
258
259             if (!(x % rulerStep)) {
260                 context.save();
261                 context.translate(x, 0);
262                 context.fillText(x, 2, -rulerWidth / 2);
263                 context.restore();
264                 y = -rulerWidth;
265             }
266
267             context.beginPath();
268             context.moveTo(x, y);
269             context.lineTo(x, 0);
270             context.stroke();
271         }
272
273         // Draw horizontal ruler.
274         for (var y = 0; y < dipGridHeight; y += rulerSubStep) {
275             x = -rulerWidth / 4;
276             if (!(y % (rulerStep / 4)))
277                 x = -rulerWidth / 2;
278             if (!(y % (rulerStep / 2)))
279                 x = -rulerWidth + 2;
280
281             if (!(y % rulerStep)) {
282                 context.save();
283                 context.translate(0, y);
284                 context.rotate(-Math.PI / 2);
285                 context.fillText(y, 2, -rulerWidth / 2);
286                 context.restore();
287                 x = -rulerWidth;
288             }
289
290             context.beginPath();
291             context.moveTo(x, y);
292             context.lineTo(0, y);
293             context.stroke();
294         }
295
296         // Draw grid.
297         drawGrid(darkLineColor, gridSubStep);
298         drawGrid(lightLineColor, gridStep);
299
300         /**
301          * @param {string} color
302          * @param {number} step
303          */
304         function drawGrid(color, step)
305         {
306             context.strokeStyle = color;
307             for (var x = 0; x < dipGridWidth; x += step) {
308                 context.beginPath();
309                 context.moveTo(x, 0);
310                 context.lineTo(x, dipGridHeight);
311                 context.stroke();
312             }
313             for (var y = 0; y < dipGridHeight; y += step) {
314                 context.beginPath();
315                 context.moveTo(0, y);
316                 context.lineTo(dipGridWidth, y);
317                 context.stroke();
318             }
319         }
320     },
321
322     _updateUI: function()
323     {
324         if (!this._enabled || !this.isShowing())
325             return;
326
327         var zoomFactor = WebInspector.zoomManager.zoomFactor();
328         var rect = this._canvas.parentElement.getBoundingClientRect();
329         var availableDip = this._availableDipSize();
330         var cssCanvasWidth = rect.width;
331         var cssCanvasHeight = rect.height;
332
333         this._widthSlider.classList.toggle("hidden", !!this._scale);
334         this._heightSlider.classList.toggle("hidden", !!this._scale);
335         this._widthSlider.classList.toggle("reversed", !this._dipWidth);
336         this._heightSlider.classList.toggle("reversed", !this._dipHeight);
337
338         if (this._cachedZoomFactor !== zoomFactor) {
339             var cssRulerWidth = WebInspector.ResponsiveDesignView.RulerWidth / zoomFactor + "px";
340             this._rulerGlasspane.style.height = cssRulerWidth;
341             this._rulerGlasspane.style.left = cssRulerWidth;
342             this._slidersContainer.style.left = cssRulerWidth;
343             this._mediaInspector.translateZero(WebInspector.ResponsiveDesignView.RulerWidth / zoomFactor);
344             this._slidersContainer.style.top = cssRulerWidth;
345             this._warningMessage.style.height = cssRulerWidth;
346
347             var cssSliderWidth = WebInspector.ResponsiveDesignView.SliderWidth / zoomFactor + "px";
348             this._heightSliderContainer.style.flexBasis = cssSliderWidth;
349             this._heightSliderContainer.style.marginBottom = "-" + cssSliderWidth;
350             this._widthSliderContainer.style.flexBasis = cssSliderWidth;
351             this._widthSliderContainer.style.marginRight = "-" + cssSliderWidth;
352         }
353
354         var cssWidth = this._dipWidth ? (this._dipWidth / zoomFactor + "px") : (availableDip.width / zoomFactor + "px");
355         var cssHeight = this._dipHeight ? (this._dipHeight / zoomFactor + "px") : (availableDip.height / zoomFactor + "px");
356         if (this._cachedCssWidth !== cssWidth || this._cachedCssHeight !== cssHeight) {
357             this._slidersContainer.style.width = cssWidth;
358             this._slidersContainer.style.height = cssHeight;
359             this._inspectedPagePlaceholder.onResize();
360         }
361
362         if (this._cachedScale !== this._scale || this._cachedCssCanvasWidth !== cssCanvasWidth || this._cachedCssCanvasHeight !== cssCanvasHeight || this._cachedZoomFactor !== zoomFactor)
363             this._drawCanvas(cssCanvasWidth, cssCanvasHeight);
364
365         this._cachedScale = this._scale;
366         this._cachedCssCanvasWidth = cssCanvasWidth;
367         this._cachedCssCanvasHeight = cssCanvasHeight;
368         this._cachedCssHeight = cssHeight;
369         this._cachedCssWidth = cssWidth;
370         this._cachedZoomFactor = zoomFactor;
371     },
372
373     onResize: function()
374     {
375         if (!this._enabled || this._ignoreResize)
376             return;
377         var oldSize = this._availableSize;
378         delete this._availableSize;
379         var newSize = this._availableDipSize();
380         if (!newSize.isEqual(oldSize))
381             this.dispatchEventToListeners(WebInspector.OverridesSupport.PageResizer.Events.AvailableSizeChanged, newSize);
382         this._updateUI();
383         this._inspectedPagePlaceholder.onResize();
384     },
385
386     _onZoomChanged: function()
387     {
388         this._updateUI();
389     },
390
391     _createToolbar: function()
392     {
393         this._toolbarElement = this._responsiveDesignContainer.element.createChild("div", "responsive-design-toolbar");
394         this._createButtonsSection();
395         this._toolbarElement.createChild("div", "responsive-design-separator");
396         this._createDeviceSection();
397         if (WebInspector.experimentsSettings.networkConditions.isEnabled()) {
398             this._toolbarElement.createChild("div", "responsive-design-separator");
399             this._createNetworkSection();
400         }
401         this._toolbarElement.createChild("div", "responsive-design-separator");
402
403         var moreButtonContainer = this._toolbarElement.createChild("div", "responsive-design-more-button-container");
404         var moreButton = moreButtonContainer.createChild("button", "responsive-design-more-button");
405         moreButton.title = WebInspector.UIString("More overrides");
406         moreButton.addEventListener("click", this._showEmulationInDrawer.bind(this), false);
407         moreButton.textContent = "\u2026";
408     },
409
410     _createButtonsSection: function()
411     {
412         var buttonsSection = this._toolbarElement.createChild("div", "responsive-design-section responsive-design-section-buttons");
413
414         var resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Reset all overrides."), "clear-status-bar-item");
415         buttonsSection.appendChild(resetButton.element);
416         resetButton.addEventListener("click", WebInspector.overridesSupport.reset, WebInspector.overridesSupport);
417
418         // Media Query Inspector.
419         this._toggleMediaInspectorButton = new WebInspector.StatusBarButton(WebInspector.UIString("Media queries."), "responsive-design-toggle-media-inspector");
420         this._toggleMediaInspectorButton.toggled = WebInspector.settings.showMediaQueryInspector.get();
421         this._toggleMediaInspectorButton.addEventListener("click", this._onToggleMediaInspectorButtonClick, this);
422         WebInspector.settings.showMediaQueryInspector.addChangeListener(this._updateMediaQueryInspector, this);
423         buttonsSection.appendChild(this._toggleMediaInspectorButton.element);
424     },
425
426     _createDeviceSection: function()
427     {
428         var deviceSection = this._toolbarElement.createChild("div", "responsive-design-section responsive-design-section-device");
429
430         // Device.
431         var deviceElement = deviceSection.createChild("div", "responsive-design-suite responsive-design-suite-top").createChild("div");
432         var fieldsetElement = deviceElement.createChild("fieldset");
433         fieldsetElement.createChild("label").textContent = WebInspector.UIString("Device");
434         fieldsetElement.appendChild(WebInspector.overridesSupport.createDeviceSelect(document));
435
436         var separator = deviceSection.createChild("div", "responsive-design-section-separator");
437
438         var detailsElement = deviceSection.createChild("div", "responsive-design-suite");
439
440         // Dimensions.
441         var screenElement = detailsElement.createChild("div", "");
442         fieldsetElement = screenElement.createChild("fieldset");
443         var resolutionButton = new WebInspector.StatusBarButton(WebInspector.UIString("Screen resolution"), "responsive-design-icon responsive-design-icon-resolution");
444         resolutionButton.setEnabled(false);
445         fieldsetElement.appendChild(resolutionButton.element);
446         fieldsetElement.appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceWidth, true, 4, "3em", WebInspector.OverridesSupport.deviceSizeValidator, true, true, WebInspector.UIString("\u2013")));
447         fieldsetElement.appendChild(document.createTextNode(" \u00D7 "));
448         fieldsetElement.appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceHeight, true, 4, "3em", WebInspector.OverridesSupport.deviceSizeValidator, true, true, WebInspector.UIString("\u2013")));
449
450         var swapButton = new WebInspector.StatusBarButton(WebInspector.UIString("Swap dimensions"), "responsive-design-icon responsive-design-icon-swap");
451         swapButton.element.tabIndex = -1;
452         swapButton.addEventListener("click", WebInspector.overridesSupport.swapDimensions, WebInspector.overridesSupport);
453         fieldsetElement.appendChild(swapButton.element);
454
455         // Device pixel ratio.
456         detailsElement.createChild("div", "responsive-design-suite-separator");
457         var dprElement = detailsElement.createChild("div", "");
458         fieldsetElement = dprElement.createChild("fieldset");
459         var dprButton = new WebInspector.StatusBarButton(WebInspector.UIString("Device pixel ratio"), "responsive-design-icon responsive-design-icon-dpr");
460         dprButton.setEnabled(false);
461         fieldsetElement.appendChild(dprButton.element);
462         fieldsetElement.appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceScaleFactor, true, 4, "2.5em", WebInspector.OverridesSupport.deviceScaleFactorValidator, true, true, WebInspector.UIString("\u2013")));
463     },
464
465     _createNetworkSection: function()
466     {
467         var networkSection = this._toolbarElement.createChild("div", "responsive-design-section responsive-design-section-network");
468
469         // Bandwidth.
470         var bandwidthElement = networkSection.createChild("div", "responsive-design-suite responsive-design-suite-top").createChild("div");
471         var fieldsetElement = bandwidthElement.createChild("fieldset");
472         var networkCheckbox = fieldsetElement.createChild("label");
473         networkCheckbox.textContent = WebInspector.UIString("Network");
474         fieldsetElement.appendChild(WebInspector.overridesSupport.createNetworkThroughputSelect(document));
475
476         var separator = networkSection.createChild("div", "responsive-design-section-separator");
477
478         // User agent.
479         var userAgentElement = networkSection.createChild("div", "responsive-design-suite").createChild("div");
480         fieldsetElement = userAgentElement.createChild("fieldset");
481         fieldsetElement.appendChild(WebInspector.SettingsUI.createSettingInputField("UA", WebInspector.overridesSupport.settings.userAgent, false, 0, "", undefined, false, false, WebInspector.UIString("No override")));
482     },
483
484     _onToggleMediaInspectorButtonClick: function()
485     {
486         WebInspector.settings.showMediaQueryInspector.set(!this._toggleMediaInspectorButton.toggled);
487     },
488
489     _updateMediaQueryInspector: function()
490     {
491         this._toggleMediaInspectorButton.toggled = WebInspector.settings.showMediaQueryInspector.get();
492         if (this._mediaInspector.isShowing() === WebInspector.settings.showMediaQueryInspector.get())
493             return;
494         if (this._mediaInspector.isShowing())
495             this._mediaInspector.detach();
496         else
497             this._mediaInspector.show(this._mediaInspectorContainer);
498         this.onResize();
499     },
500
501     _overridesWarningUpdated: function()
502     {
503         var message = WebInspector.overridesSupport.warningMessage();
504         if (this._warningMessage.querySelector("span").textContent === message)
505             return;
506         this._warningMessage.classList.toggle("hidden", !message);
507         this._warningMessage.querySelector("span").textContent = message;
508         this._invalidateCache();
509         this.onResize();
510     },
511
512     _showEmulationInDrawer: function()
513     {
514         WebInspector.overridesSupport.reveal();
515     },
516
517     __proto__: WebInspector.VBox.prototype
518 };