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