Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / ui / 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.VBox}
34  */
35 WebInspector.InspectorView = function()
36 {
37     WebInspector.VBox.call(this);
38     WebInspector.Dialog.setModalHostView(this);
39     this.setMinimumSize(180, 72);
40
41     // DevTools sidebar is a vertical split of panels tabbed pane and a drawer.
42     this._drawerSplitView = new WebInspector.SplitView(false, true, "Inspector.drawerSplitViewState", 200, 200);
43     this._drawerSplitView.hideSidebar();
44     this._drawerSplitView.enableShowModeSaving();
45     this._drawerSplitView.show(this.element);
46
47     this._tabbedPane = new WebInspector.TabbedPane();
48     this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(WebInspector.Panel, "name", "order"));
49     this._tabbedPane.show(this._drawerSplitView.mainElement());
50     this._drawer = new WebInspector.Drawer(this._drawerSplitView);
51
52     // Patch tabbed pane header with toolbar actions.
53     this._toolbarElement = document.createElement("div");
54     this._toolbarElement.className = "toolbar toolbar-background";
55     var headerElement = this._tabbedPane.headerElement();
56     headerElement.parentElement.insertBefore(this._toolbarElement, headerElement);
57
58     this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left");
59     this._toolbarElement.appendChild(headerElement);
60     this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right");
61
62     this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden");
63     this._errorWarningCountElement.id = "error-warning-count";
64
65     this._closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
66     var closeButtonElement = this._closeButtonToolbarItem.createChild("div", "close-button");
67     closeButtonElement.addEventListener("click", InspectorFrontendHost.closeWindow.bind(InspectorFrontendHost), true);
68     this._rightToolbarElement.appendChild(this._closeButtonToolbarItem);
69
70     this.appendToRightToolbar(this._drawer.toggleButtonElement());
71
72     this._panels = {};
73     // Used by tests.
74     WebInspector["panels"] = this._panels;
75
76     this._history = [];
77     this._historyIterator = -1;
78     document.addEventListener("keydown", this._keyDown.bind(this), false);
79     document.addEventListener("keypress", this._keyPress.bind(this), false);
80     this._panelDescriptors = {};
81
82     // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
83     this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
84     this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
85     this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements");
86
87     this._loadPanelDesciptors();
88 };
89
90 WebInspector.InspectorView.prototype = {
91     _loadPanelDesciptors: function()
92     {
93         WebInspector.startBatchUpdate();
94         WebInspector.moduleManager.extensions(WebInspector.Panel).forEach(processPanelExtensions.bind(this));
95         /**
96          * @param {!WebInspector.ModuleManager.Extension} extension
97          * @this {!WebInspector.InspectorView}
98          */
99         function processPanelExtensions(extension)
100         {
101             this.addPanel(new WebInspector.ModuleManagerExtensionPanelDescriptor(extension));
102         }
103         WebInspector.endBatchUpdate();
104     },
105
106     /**
107      * @param {!Element} element
108      */
109     appendToLeftToolbar: function(element)
110     {
111         this._leftToolbarElement.appendChild(element);
112     },
113
114     /**
115      * @param {!Element} element
116      */
117     appendToRightToolbar: function(element)
118     {
119         this._rightToolbarElement.insertBefore(element, this._closeButtonToolbarItem);
120     },
121
122     /**
123      * @param {!WebInspector.PanelDescriptor} panelDescriptor
124      */
125     addPanel: function(panelDescriptor)
126     {
127         var panelName = panelDescriptor.name();
128         this._panelDescriptors[panelName] = panelDescriptor;
129         this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
130         if (this._lastActivePanelSetting.get() === panelName)
131             this._tabbedPane.selectTab(panelName);
132     },
133
134     /**
135      * @param {string} panelName
136      * @return {boolean}
137      */
138     hasPanel: function(panelName)
139     {
140         return !!this._panelDescriptors[panelName];
141     },
142
143     /**
144      * @param {string} panelName
145      * @return {?WebInspector.Panel}
146      */
147     panel: function(panelName)
148     {
149         var panelDescriptor = this._panelDescriptors[panelName];
150         var panelOrder = this._tabbedPane.allTabs();
151         if (!panelDescriptor && panelOrder.length)
152             panelDescriptor = this._panelDescriptors[panelOrder[0]];
153         var panel = panelDescriptor ? panelDescriptor.panel() : null;
154         if (panel)
155             this._panels[panelName] = panel;
156         return panel;
157     },
158
159     /**
160      * @param {string} panelName
161      * @return {?WebInspector.Panel}
162      */
163     showPanel: function(panelName)
164     {
165         var panel = this.panel(panelName);
166         if (panel)
167             this.setCurrentPanel(panel);
168         return panel;
169     },
170
171     /**
172      * @return {!WebInspector.Panel}
173      */
174     currentPanel: function()
175     {
176         return this._currentPanel;
177     },
178
179     showInitialPanel: function()
180     {
181         this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
182         this._tabSelected();
183         this._drawer.initialPanelShown();
184     },
185
186     showDrawerEditor: function()
187     {
188         this._drawer.showDrawerEditor();
189     },
190
191     /**
192      * @return {boolean}
193      */
194     isDrawerEditorShown: function()
195     {
196         return this._drawer.isDrawerEditorShown();
197     },
198
199     hideDrawerEditor: function()
200     {
201         this._drawer.hideDrawerEditor();
202     },
203
204     /**
205      * @param {boolean} available
206      */
207     setDrawerEditorAvailable: function(available)
208     {
209         this._drawer.setDrawerEditorAvailable(available);
210     },
211
212     _tabSelected: function()
213     {
214         var panelName = this._tabbedPane.selectedTabId;
215         if (!panelName)
216             return;
217         var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
218         this._panels[panelName] = panel;
219         this._tabbedPane.changeTabView(panelName, panel);
220
221         this._currentPanel = panel;
222         this._lastActivePanelSetting.set(panel.name);
223         this._pushToHistory(panel.name);
224         WebInspector.userMetrics.panelShown(panel.name);
225         panel.focus();
226     },
227
228     /**
229      * @param {!WebInspector.Panel} x
230      */
231     setCurrentPanel: function(x)
232     {
233         if (this._currentPanel === x)
234             return;
235
236         this._tabbedPane.changeTabView(x.name, x);
237         this._tabbedPane.selectTab(x.name);
238     },
239
240     /**
241      * @param {string} id
242      */
243     closeViewInDrawer: function(id)
244     {
245         this._drawer.closeView(id);
246     },
247
248     /**
249      * @param {string} id
250      * @param {string} title
251      * @param {!WebInspector.View} view
252      */
253     showCloseableViewInDrawer: function(id, title, view)
254     {
255         this._drawer.showCloseableView(id, title, view);
256     },
257
258     showDrawer: function()
259     {
260         this._drawer.showDrawer();
261     },
262
263     /**
264      * @return {boolean}
265      */
266     drawerVisible: function()
267     {
268         return this._drawer.isShowing();
269     },
270
271     /**
272      * @param {string} id
273      * @param {boolean=} immediate
274      */
275     showViewInDrawer: function(id, immediate)
276     {
277         this._drawer.showView(id, immediate);
278     },
279
280     /**
281      * @return {?string}
282      */
283     selectedViewInDrawer: function()
284     {
285         return this._drawer.selectedViewId();
286     },
287
288     closeDrawer: function()
289     {
290         this._drawer.closeDrawer();
291     },
292
293     /**
294      * @return {!Element}
295      */
296     defaultFocusedElement: function()
297     {
298         return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
299     },
300
301     _keyPress: function(event)
302     {
303         // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
304         // Any charCode < 32 is not going to be a valid keypress.
305         if (event.charCode < 32 && WebInspector.isWin())
306             return;
307         clearTimeout(this._keyDownTimer);
308         delete this._keyDownTimer;
309     },
310
311     _keyDown: function(event)
312     {
313         if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
314             return;
315
316         var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
317         // Ctrl/Cmd + 1-9 should show corresponding panel.
318         var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
319         if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
320             var panelIndex = -1;
321             if (event.keyCode > 0x30 && event.keyCode < 0x3A)
322                 panelIndex = event.keyCode - 0x31;
323             else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
324                 panelIndex = event.keyCode - 0x61;
325             if (panelIndex !== -1) {
326                 var panelName = this._tabbedPane.allTabs()[panelIndex];
327                 if (panelName) {
328                     if (!WebInspector.Dialog.currentInstance())
329                         this.showPanel(panelName);
330                     event.consume(true);
331                 }
332                 return;
333             }
334         }
335
336         // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
337         // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
338         // If there is, we cancel the timer and do not consider this a panel switch.
339         if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
340             this._keyDownInternal(event);
341             return;
342         }
343
344         this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
345     },
346
347     _keyDownInternal: function(event)
348     {
349         var direction = 0;
350
351         if (this._openBracketIdentifiers[event.keyIdentifier])
352             direction = -1;
353
354         if (this._closeBracketIdentifiers[event.keyIdentifier])
355             direction = 1;
356
357         if (!direction)
358             return;
359
360         if (!event.shiftKey && !event.altKey) {
361             if (!WebInspector.Dialog.currentInstance())
362                 this._changePanelInDirection(direction);
363             event.consume(true);
364             return;
365         }
366
367         if (event.altKey && this._moveInHistory(direction))
368             event.consume(true)
369     },
370
371     _changePanelInDirection: function(direction)
372     {
373         var panelOrder = this._tabbedPane.allTabs();
374         var index = panelOrder.indexOf(this.currentPanel().name);
375         index = (index + panelOrder.length + direction) % panelOrder.length;
376         this.showPanel(panelOrder[index]);
377     },
378
379     _moveInHistory: function(move)
380     {
381         var newIndex = this._historyIterator + move;
382         if (newIndex >= this._history.length || newIndex < 0)
383             return false;
384
385         this._inHistory = true;
386         this._historyIterator = newIndex;
387         if (!WebInspector.Dialog.currentInstance())
388             this.setCurrentPanel(this._panels[this._history[this._historyIterator]]);
389         delete this._inHistory;
390
391         return true;
392     },
393
394     _pushToHistory: function(panelName)
395     {
396         if (this._inHistory)
397             return;
398
399         this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
400         if (!this._history.length || this._history[this._history.length - 1] !== panelName)
401             this._history.push(panelName);
402         this._historyIterator = this._history.length - 1;
403     },
404
405     onResize: function()
406     {
407         WebInspector.Dialog.modalHostRepositioned();
408     },
409
410     /**
411      * @return {!Element}
412      */
413     topResizerElement: function()
414     {
415         return this._tabbedPane.headerElement();
416     },
417
418     _createImagedCounterElementIfNeeded: function(count, id, styleName)
419     {
420         if (!count)
421             return;
422
423         var imageElement = this._errorWarningCountElement.createChild("div", styleName);
424         var counterElement = this._errorWarningCountElement.createChild("span");
425         counterElement.id = id;
426         counterElement.textContent = count;
427     },
428
429     /**
430      * @param {number} errors
431      * @param {number} warnings
432      */
433     setErrorAndWarningCounts: function(errors, warnings)
434     {
435         if (this._errors === errors && this._warnings === warnings)
436             return;
437         this._errors = errors;
438         this._warnings = warnings;
439         this._errorWarningCountElement.classList.toggle("hidden", !errors && !warnings);
440         this._errorWarningCountElement.removeChildren();
441
442         this._createImagedCounterElementIfNeeded(errors, "error-count", "error-icon-small");
443         this._createImagedCounterElementIfNeeded(warnings, "warning-count", "warning-icon-small");
444
445         var errorString = errors ?  WebInspector.UIString("%d error%s", errors, errors > 1 ? "s" : "") : "";
446         var warningString = warnings ?  WebInspector.UIString("%d warning%s", warnings, warnings > 1 ? "s" : "") : "";
447         var commaString = errors && warnings ? ", " : "";
448         this._errorWarningCountElement.title = errorString + commaString + warningString;
449         this._tabbedPane.headerResized();
450     },
451
452     __proto__: WebInspector.VBox.prototype
453 };
454
455 /**
456  * @type {!WebInspector.InspectorView}
457  */
458 WebInspector.inspectorView;
459
460 /**
461  * @constructor
462  * @implements {WebInspector.ActionDelegate}
463  */
464 WebInspector.InspectorView.DrawerToggleActionDelegate = function()
465 {
466 }
467
468 WebInspector.InspectorView.DrawerToggleActionDelegate.prototype = {
469     /**
470      * @return {boolean}
471      */
472     handleAction: function()
473     {
474         if (WebInspector.inspectorView.drawerVisible()) {
475             WebInspector.inspectorView.closeDrawer();
476             return true;
477         }
478         WebInspector.inspectorView.showDrawer();
479         return true;
480     }
481 }
482
483 /**
484  * @constructor
485  * @extends {WebInspector.VBox}
486  */
487 WebInspector.RootView = function()
488 {
489     WebInspector.VBox.call(this);
490     this.markAsRoot();
491     this.element.classList.add("root-view");
492     this.element.setAttribute("spellcheck", false);
493     window.addEventListener("resize", this.doResize.bind(this), true);
494     this._onScrollBound = this._onScroll.bind(this);
495 };
496
497 WebInspector.RootView.prototype = {
498     attachToBody: function()
499     {
500         this.doResize();
501         this.show(document.body);
502     },
503
504     _onScroll: function()
505     {
506         // If we didn't have enough space at the start, we may have wrong scroll offsets.
507         if (document.body.scrollTop !== 0)
508             document.body.scrollTop = 0;
509         if (document.body.scrollLeft !== 0)
510             document.body.scrollLeft = 0;
511     },
512
513     doResize: function()
514     {
515         var size = this.constraints().minimum;
516         var right = Math.min(0, window.innerWidth - size.width);
517         this.element.style.right = right + "px";
518         var bottom = Math.min(0, window.innerHeight - size.height);
519         this.element.style.bottom = bottom + "px";
520
521         if (window.innerWidth < size.width || window.innerHeight < size.height)
522             window.addEventListener("scroll", this._onScrollBound, false);
523         else
524             window.removeEventListener("scroll", this._onScrollBound, false);
525
526         WebInspector.VBox.prototype.doResize.call(this);
527         this._onScroll();
528     },
529
530     __proto__: WebInspector.VBox.prototype
531 };