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