Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / InspectorView.js
1 /*
2  * Copyright (C) 2011 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.View}
34  */
35 WebInspector.InspectorView = function()
36 {
37     WebInspector.View.call(this);
38     this.markAsRoot();
39     this.element.classList.add("fill", "inspector-view");
40     this.element.setAttribute("spellcheck", false);
41
42     window.addEventListener("resize", this._onWindowResize.bind(this), true);
43     WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
44
45     // We can use split view either for docking or screencast, but not together.
46     var settingName = WebInspector.queryParamsObject["can_dock"] ? "InspectorView.splitView" : "InspectorView.screencastSplitView";
47     this._splitView = new WebInspector.SplitView(false, true, settingName, 300, 300);
48     this._updateConstraints();
49     WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._updateSplitView.bind(this));
50
51     this._splitView.element.id = "inspector-split-view";
52     this._splitView.show(this.element);
53
54     // Main part of main split is overlay view.
55     this._overlayView = new WebInspector.InspectorView.OverlayView(this._splitView);
56     this._overlayView.show(this._splitView.mainElement());
57
58     // Sidebar of main split is artificial element used for positioning.
59     this._devtoolsView = new WebInspector.ViewWithResizeCallback(this._onDevToolsViewResized.bind(this));
60     this._devtoolsView.show(this._splitView.sidebarElement());
61     WebInspector.Dialog.setModalHostView(this._devtoolsView);
62
63     // DevTools sidebar is a vertical split of panels tabbed pane and a drawer.
64     this._drawerSplitView = new WebInspector.SplitView(false, true, "Inspector.drawerSplitView", 200, 200);
65     this._drawerSplitView.setSidebarElementConstraints(Preferences.minDrawerHeight, Preferences.minDrawerHeight);
66     this._drawerSplitView.setMainElementConstraints(25, 25);
67     this._drawerSplitView.show(this._devtoolsView.element);
68
69     this._tabbedPane = new WebInspector.TabbedPane();
70     this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(WebInspector.Panel, "name", "order"));
71     this._tabbedPane.show(this._drawerSplitView.mainElement());
72     this._drawer = new WebInspector.Drawer(this._drawerSplitView);
73
74     // Patch tabbed pane header with toolbar actions.
75     this._toolbarElement = document.createElement("div");
76     this._toolbarElement.className = "toolbar toolbar-background";
77     var headerElement = this._tabbedPane.headerElement();
78     headerElement.parentElement.insertBefore(this._toolbarElement, headerElement);
79
80     this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left");
81     this._toolbarElement.appendChild(headerElement);
82     this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right");
83
84     this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden");
85     this._errorWarningCountElement.id = "error-warning-count";
86
87     this._closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
88     var closeButtonElement = this._closeButtonToolbarItem.createChild("div", "close-button");
89     closeButtonElement.addEventListener("click", WebInspector.close.bind(WebInspector), true);
90     this._rightToolbarElement.appendChild(this._closeButtonToolbarItem);
91
92     this.appendToRightToolbar(this._drawer.toggleButtonElement());
93
94     this._history = [];
95     this._historyIterator = -1;
96     document.addEventListener("keydown", this._keyDown.bind(this), false);
97     document.addEventListener("keypress", this._keyPress.bind(this), false);
98     this._panelDescriptors = {};
99
100     // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
101     this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
102     this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
103     this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements");
104
105     this._updateSplitView();
106
107     this._loadPanelDesciptors();
108 }
109
110 WebInspector.InspectorView.Constraints = {
111     OverlayWidth: 50,
112     OverlayHeight: 50,
113     DevToolsWidth: 180,
114     DevToolsHeight: 50
115 };
116
117 WebInspector.InspectorView.prototype = {
118     _loadPanelDesciptors: function()
119     {
120         WebInspector.startBatchUpdate();
121         WebInspector.moduleManager.extensions(WebInspector.Panel).forEach(processPanelExtensions.bind(this));
122         /**
123          * @param {!WebInspector.ModuleManager.Extension} extension
124          * @this {!WebInspector.InspectorView}
125          */
126         function processPanelExtensions(extension)
127         {
128             this.addPanel(new WebInspector.ModuleManagerExtensionPanelDescriptor(extension));
129         }
130         WebInspector.endBatchUpdate();
131     },
132
133     /**
134      * @param {!Element} element
135      */
136     appendToLeftToolbar: function(element)
137     {
138         this._leftToolbarElement.appendChild(element);
139     },
140
141     /**
142      * @param {!Element} element
143      */
144     appendToRightToolbar: function(element)
145     {
146         this._rightToolbarElement.insertBefore(element, this._closeButtonToolbarItem);
147     },
148
149     /**
150      * @return {!Element}
151      */
152     devtoolsElement: function()
153     {
154         return this._devtoolsView.element;
155     },
156
157     /**
158      * @param {!WebInspector.PanelDescriptor} panelDescriptor
159      */
160     addPanel: function(panelDescriptor)
161     {
162         var panelName = panelDescriptor.name();
163         this._panelDescriptors[panelName] = panelDescriptor;
164         this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
165         if (this._lastActivePanelSetting.get() === panelName)
166             this._tabbedPane.selectTab(panelName);
167     },
168
169     /**
170      * @param {string} panelName
171      * @return {?WebInspector.Panel}
172      */
173     panel: function(panelName)
174     {
175         var panelDescriptor = this._panelDescriptors[panelName];
176         var panelOrder = this._tabbedPane.allTabs();
177         if (!panelDescriptor && panelOrder.length)
178             panelDescriptor = this._panelDescriptors[panelOrder[0]];
179         return panelDescriptor ? panelDescriptor.panel() : null;
180     },
181
182     /**
183      * @param {string} panelName
184      * @return {?WebInspector.Panel}
185      */
186     showPanel: function(panelName)
187     {
188         var panel = this.panel(panelName);
189         if (panel)
190             this.setCurrentPanel(panel);
191         return panel;
192     },
193
194     /**
195      * @return {!WebInspector.Panel}
196      */
197     currentPanel: function()
198     {
199         return this._currentPanel;
200     },
201
202     showInitialPanel: function()
203     {
204         this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
205         this._tabSelected();
206         this._drawer.showOnLoadIfNecessary();
207     },
208
209     _tabSelected: function()
210     {
211         var panelName = this._tabbedPane.selectedTabId;
212         var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
213         this._tabbedPane.changeTabView(panelName, panel);
214
215         this._currentPanel = panel;
216         this._lastActivePanelSetting.set(panel.name);
217         this._pushToHistory(panel.name);
218         WebInspector.userMetrics.panelShown(panel.name);
219         panel.focus();
220     },
221
222     /**
223      * @param {!WebInspector.Panel} x
224      */
225     setCurrentPanel: function(x)
226     {
227         if (this._currentPanel === x)
228             return;
229
230         this._tabbedPane.changeTabView(x.name, x);
231         this._tabbedPane.selectTab(x.name);
232     },
233
234     /**
235      * @param {string} id
236      */
237     closeViewInDrawer: function(id)
238     {
239         this._drawer.closeView(id);
240     },
241
242     /**
243      * @param {string} id
244      * @param {string} title
245      * @param {!WebInspector.View} view
246      */
247     showCloseableViewInDrawer: function(id, title, view)
248     {
249         this._drawer.showCloseableView(id, title, view);
250     },
251
252     showDrawer: function()
253     {
254         this._drawer.showDrawer();
255     },
256
257     /**
258      * @return {boolean}
259      */
260     drawerVisible: function()
261     {
262         return this._drawer.isShowing();
263     },
264
265     /**
266      * @param {string} id
267      * @param {boolean=} immediate
268      */
269     showViewInDrawer: function(id, immediate)
270     {
271         this._drawer.showView(id, immediate);
272     },
273
274     /**
275      * @return {string}
276      */
277     selectedViewInDrawer: function()
278     {
279         return this._drawer.selectedViewId();
280     },
281
282     closeDrawer: function()
283     {
284         this._drawer.closeDrawer();
285     },
286
287     /**
288      * @return {!Element}
289      */
290     defaultFocusedElement: function()
291     {
292         return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
293     },
294
295     _keyPress: function(event)
296     {
297         // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
298         // Any charCode < 32 is not going to be a valid keypress.
299         if (event.charCode < 32 && WebInspector.isWin())
300             return;
301         clearTimeout(this._keyDownTimer);
302         delete this._keyDownTimer;
303     },
304
305     _keyDown: function(event)
306     {
307         if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
308             return;
309
310         var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
311         // Ctrl/Cmd + 1-9 should show corresponding panel.
312         var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
313         if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
314             var panelIndex = -1;
315             if (event.keyCode > 0x30 && event.keyCode < 0x3A)
316                 panelIndex = event.keyCode - 0x31;
317             else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
318                 panelIndex = event.keyCode - 0x61;
319             if (panelIndex !== -1) {
320                 var panelName = this._tabbedPane.allTabs()[panelIndex];
321                 if (panelName) {
322                     this.showPanel(panelName);
323                     event.consume(true);
324                 }
325                 return;
326             }
327         }
328
329         // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
330         // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
331         // If there is, we cancel the timer and do not consider this a panel switch.
332         if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
333             this._keyDownInternal(event);
334             return;
335         }
336
337         this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
338     },
339
340     _keyDownInternal: function(event)
341     {
342         if (this._openBracketIdentifiers[event.keyIdentifier]) {
343             var isRotateLeft = !event.shiftKey && !event.altKey;
344             if (isRotateLeft) {
345                 var panelOrder = this._tabbedPane.allTabs();
346                 var index = panelOrder.indexOf(this.currentPanel().name);
347                 index = (index === 0) ? panelOrder.length - 1 : index - 1;
348                 this.showPanel(panelOrder[index]);
349                 event.consume(true);
350                 return;
351             }
352
353             var isGoBack = event.altKey;
354             if (isGoBack && this._canGoBackInHistory()) {
355                 this._goBackInHistory();
356                 event.consume(true);
357             }
358             return;
359         }
360
361         if (this._closeBracketIdentifiers[event.keyIdentifier]) {
362             var isRotateRight = !event.shiftKey && !event.altKey;
363             if (isRotateRight) {
364                 var panelOrder = this._tabbedPane.allTabs();
365                 var index = panelOrder.indexOf(this.currentPanel().name);
366                 index = (index + 1) % panelOrder.length;
367                 this.showPanel(panelOrder[index]);
368                 event.consume(true);
369                 return;
370             }
371
372             var isGoForward = event.altKey;
373             if (isGoForward && this._canGoForwardInHistory()) {
374                 this._goForwardInHistory();
375                 event.consume(true);
376             }
377             return;
378         }
379     },
380
381     _canGoBackInHistory: function()
382     {
383         return this._historyIterator > 0;
384     },
385
386     _goBackInHistory: function()
387     {
388         this._inHistory = true;
389         this.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]);
390         delete this._inHistory;
391     },
392
393     _canGoForwardInHistory: function()
394     {
395         return this._historyIterator < this._history.length - 1;
396     },
397
398     _goForwardInHistory: function()
399     {
400         this._inHistory = true;
401         this.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]);
402         delete this._inHistory;
403     },
404
405     _pushToHistory: function(panelName)
406     {
407         if (this._inHistory)
408             return;
409
410         this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
411         if (!this._history.length || this._history[this._history.length - 1] !== panelName)
412             this._history.push(panelName);
413         this._historyIterator = this._history.length - 1;
414     },
415
416     _onDevToolsViewResized: function()
417     {
418         WebInspector.Dialog.modalHostRepositioned();
419     },
420
421     _onWindowResize: function()
422     {
423         this.doResize();
424     },
425
426     _updateSplitView: function()
427     {
428         var dockSide = WebInspector.dockController.dockSide();
429         if (dockSide !== WebInspector.DockController.State.Undocked) {
430             var vertical = WebInspector.dockController.isVertical();
431             this._splitView.setVertical(vertical);
432             if (vertical) {
433                 // Docked to side.
434                 if (dockSide === WebInspector.DockController.State.DockedToRight)
435                     this._overlayView.setMargins(false, true, false, false);
436                 else
437                     this._overlayView.setMargins(false, false, false, true);
438                 this._splitView.setSecondIsSidebar(dockSide === WebInspector.DockController.State.DockedToRight);
439                 this._splitView.uninstallResizer(this._tabbedPane.headerElement());
440                 this._splitView.installResizer(this._splitView.resizerElement());
441             } else {
442                 // Docked to bottom.
443                 this._overlayView.setMargins(false, false, false, false);
444                 this._splitView.setSecondIsSidebar(true);
445                 this._splitView.installResizer(this._splitView.resizerElement());
446                 this._splitView.installResizer(this._tabbedPane.headerElement());
447             }
448             this._splitView.showBoth();
449         } else {
450             this._overlayView.setMargins(false, false, false, false);
451             this._splitView.hideMain();
452             this._splitView.uninstallResizer(this._tabbedPane.headerElement());
453             this._splitView.uninstallResizer(this._splitView.resizerElement());
454         }
455     },
456
457     _onZoomChanged: function(event)
458     {
459         this._updateConstraints();
460         var data = /** @type {{from: number, to: number}} */ (event.data);
461         this._splitView.setSidebarSize(this._splitView.sidebarSize() * data.from / data.to, true);
462         this._overlayView.updateMargins();
463     },
464
465     _updateConstraints: function()
466     {
467         var zoomFactor = WebInspector.zoomManager.zoomFactor();
468         this._splitView.setSidebarElementConstraints(WebInspector.InspectorView.Constraints.DevToolsWidth / zoomFactor,
469             WebInspector.InspectorView.Constraints.DevToolsHeight / zoomFactor);
470         this._splitView.setMainElementConstraints(WebInspector.InspectorView.Constraints.OverlayWidth / zoomFactor,
471             WebInspector.InspectorView.Constraints.OverlayHeight / zoomFactor);
472     },
473
474     /**
475      * @param {!WebInspector.View} view
476      * @param {boolean} vertical
477      */
478     showScreencastView: function(view, vertical)
479     {
480         if (view.parentView() !== this._overlayView)
481             view.show(this._overlayView.element);
482         this._splitView.setVertical(vertical);
483         this._splitView.installResizer(this._splitView.resizerElement());
484         this._splitView.showBoth();
485     },
486
487     hideScreencastView: function()
488     {
489         this._splitView.hideMain();
490     },
491
492     /**
493      * @param {number} errors
494      * @param {number} warnings
495      */
496     setErrorAndWarningCounts: function(errors, warnings)
497     {
498         if (!errors && !warnings) {
499             this._errorWarningCountElement.classList.add("hidden");
500             this._tabbedPane.headerResized();
501             return;
502         }
503
504         this._errorWarningCountElement.classList.remove("hidden");
505         this._errorWarningCountElement.removeChildren();
506
507         if (errors) {
508             var errorImageElement = this._errorWarningCountElement.createChild("div", "error-icon-small");
509             var errorElement = this._errorWarningCountElement.createChild("span");
510             errorElement.id = "error-count";
511             errorElement.textContent = errors;
512         }
513
514         if (warnings) {
515             var warningsImageElement = this._errorWarningCountElement.createChild("div", "warning-icon-small");
516             var warningsElement = this._errorWarningCountElement.createChild("span");
517             warningsElement.id = "warning-count";
518             warningsElement.textContent = warnings;
519         }
520
521         if (errors) {
522             if (warnings) {
523                 if (errors == 1) {
524                     if (warnings == 1)
525                         this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
526                     else
527                         this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
528                 } else if (warnings == 1)
529                     this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
530                 else
531                     this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
532             } else if (errors == 1)
533                 this._errorWarningCountElement.title = WebInspector.UIString("%d error", errors);
534             else
535                 this._errorWarningCountElement.title = WebInspector.UIString("%d errors", errors);
536         } else if (warnings == 1)
537             this._errorWarningCountElement.title = WebInspector.UIString("%d warning", warnings);
538         else if (warnings)
539             this._errorWarningCountElement.title = WebInspector.UIString("%d warnings", warnings);
540         else
541             this._errorWarningCountElement.title = null;
542
543         this._tabbedPane.headerResized();
544     },
545
546     __proto__: WebInspector.View.prototype
547 };
548
549 /**
550  * @constructor
551  * @param {!WebInspector.SplitView} splitView
552  * @extends {WebInspector.View}
553  */
554 WebInspector.InspectorView.OverlayView = function(splitView)
555 {
556     WebInspector.View.call(this);
557     this._margins = {top: 0, left: 0, right: 0, bottom: 0};
558     this._splitView = splitView;
559 }
560
561 WebInspector.InspectorView.OverlayView.prototype = {
562     /**
563      * @param {boolean} top
564      * @param {boolean} right
565      * @param {boolean} bottom
566      * @param {boolean} left
567      */
568     setMargins: function(top, right, bottom, left)
569     {
570         this._margins = { top: top, right: right, bottom: bottom, left: left };
571         this.updateMargins();
572     },
573
574     updateMargins: function()
575     {
576         var marginValue = Math.round(3 / WebInspector.zoomManager.zoomFactor()) + "px ";
577         var margins = this._margins.top ? marginValue : "0 ";
578         margins += this._margins.right ? marginValue : "0 ";
579         margins += this._margins.bottom ? marginValue : "0 ";
580         margins += this._margins.left ? marginValue : "0 ";
581         this.element.style.margin = margins;
582     },
583
584     onResize: function()
585     {
586         var dockSide = WebInspector.dockController.dockSide();
587         if (dockSide !== WebInspector.DockController.State.Undocked) {
588             if (this._setContentsInsetsId)
589                 window.cancelAnimationFrame(this._setContentsInsetsId);
590             this._setContentsInsetsId = window.requestAnimationFrame(this._setContentsInsets.bind(this));
591         }
592     },
593
594     _setContentsInsets: function()
595     {
596         delete this._setContentsInsetsId;
597
598         var zoomFactor = WebInspector.zoomManager.zoomFactor();
599         var marginValue = Math.round(3 / zoomFactor);
600         var insets = {
601             top: this._margins.top ? marginValue : 0,
602             left: this._margins.left ? marginValue : 0,
603             right: this._margins.right ? marginValue : 0,
604             bottom: this._margins.bottom ? marginValue : 0};
605
606         var minSize = {
607             width: WebInspector.InspectorView.Constraints.OverlayWidth - Math.round(insets.left * zoomFactor) - Math.round(insets.right * zoomFactor),
608             height: WebInspector.InspectorView.Constraints.OverlayHeight - Math.round(insets.top * zoomFactor) - Math.round(insets.bottom * zoomFactor)};
609
610         insets[this._splitView.sidebarSide()] += this._splitView.desiredSidebarSize();
611
612         var zoomedInsets = {
613             top: Math.round(insets.top * zoomFactor),
614             left: Math.round(insets.left * zoomFactor),
615             bottom: Math.round(insets.bottom * zoomFactor),
616             right: Math.round(insets.right * zoomFactor)};
617
618         InspectorFrontendHost.setContentsResizingStrategy(zoomedInsets, minSize);
619     },
620
621     __proto__: WebInspector.View.prototype
622 }
623
624 /**
625  * @type {!WebInspector.InspectorView}
626  */
627 WebInspector.inspectorView;