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