2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.VBox}
35 WebInspector.InspectorView = function()
37 WebInspector.VBox.call(this);
38 WebInspector.Dialog.setModalHostView(this);
39 WebInspector.GlassPane.DefaultFocusedViewStack.push(this);
40 this.setMinimumSize(240, 72);
42 // DevTools sidebar is a vertical split of panels tabbed pane and a drawer.
43 this._drawerSplitWidget = new WebInspector.SplitWidget(false, true, "Inspector.drawerSplitViewState", 200, 200);
44 this._drawerSplitWidget.hideSidebar();
45 this._drawerSplitWidget.enableShowModeSaving();
46 this._drawerSplitWidget.show(this.element);
48 this._tabbedPane = new WebInspector.TabbedPane();
49 this._tabbedPane.registerRequiredCSS("components/inspectorViewTabbedPane.css");
50 this._tabbedPane.element.classList.add("inspector-view-tabbed-pane");
51 this._tabbedPane.setTabSlider(true);
52 this._tabbedPane.setAllowTabReorder(true);
53 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabOrderChanged, this._persistPanelOrder, this);
54 this._tabOrderSetting = WebInspector.settings.createSetting("InspectorView.panelOrder", {});
55 this._drawerSplitWidget.setMainWidget(this._tabbedPane);
56 this._drawer = new WebInspector.Drawer(this._drawerSplitWidget);
60 WebInspector["panels"] = this._panels;
63 this._historyIterator = -1;
64 this._keyDownBound = this._keyDown.bind(this);
65 this._keyPressBound = this._keyPress.bind(this);
66 /** @type {!Object.<string, !WebInspector.PanelDescriptor>} */
67 this._panelDescriptors = {};
68 /** @type {!Object.<string, !Promise.<!WebInspector.Panel> >} */
69 this._panelPromises = {};
71 // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
72 this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
73 this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
74 this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements");
76 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ShowConsole, showConsole.bind(this));
77 this._loadPanelDesciptors();
80 * @this {WebInspector.InspectorView}
82 function showConsole()
84 this.showPanel("console");
87 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged.bind(this));
90 WebInspector.InspectorView.prototype = {
93 this.element.ownerDocument.addEventListener("keydown", this._keyDownBound, false);
94 this.element.ownerDocument.addEventListener("keypress", this._keyPressBound, false);
99 this.element.ownerDocument.removeEventListener("keydown", this._keyDownBound, false);
100 this.element.ownerDocument.removeEventListener("keypress", this._keyPressBound, false);
103 _loadPanelDesciptors: function()
106 * @param {!Runtime.Extension} extension
107 * @this {!WebInspector.InspectorView}
109 function processPanelExtensions(extension)
111 var descriptor = new WebInspector.RuntimeExtensionPanelDescriptor(extension);
112 var weight = this._tabOrderSetting.get()[descriptor.name()];
113 if (weight === undefined)
114 weight = extension.descriptor()["order"];
115 if (weight === undefined)
117 panelsByWeight.set(weight, descriptor);
120 WebInspector.startBatchUpdate();
121 /** @type {!Map.<number, !WebInspector.PanelDescriptor>} */
122 var panelsByWeight = new Map();
123 self.runtime.extensions(WebInspector.PanelFactory).forEach(processPanelExtensions.bind(this));
124 var sortedPanelOrders = panelsByWeight.keysArray().sort();
125 for (var order of sortedPanelOrders) {
126 var panelDescriptor = panelsByWeight.get(order);
128 this._innerAddPanel(panelDescriptor);
130 WebInspector.endBatchUpdate();
133 createToolbars: function()
135 this._leftToolbar = new WebInspector.ExtensibleToolbar("main-toolbar-left");
136 this._leftToolbar.element.classList.add("inspector-view-toolbar", "inspector-view-toolbar-left");
138 this._tabbedPane.insertBeforeTabStrip(this._leftToolbar.element);
140 var rightToolbarContainer = createElementWithClass("div", "hbox flex-none flex-centered");
141 this._tabbedPane.appendAfterTabStrip(rightToolbarContainer);
143 this._rightToolbar = new WebInspector.ExtensibleToolbar("main-toolbar-right");
144 this._rightToolbar.element.classList.add("inspector-view-toolbar", "flex-none");
145 rightToolbarContainer.appendChild(this._rightToolbar.element);
149 * @param {!WebInspector.PanelDescriptor} panelDescriptor
150 * @param {number=} index
152 _innerAddPanel: function(panelDescriptor, index)
154 var panelName = panelDescriptor.name();
155 this._panelDescriptors[panelName] = panelDescriptor;
156 this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.Widget(), undefined, undefined, undefined, index);
157 if (this._lastActivePanelSetting.get() === panelName)
158 this._tabbedPane.selectTab(panelName);
162 * @param {!WebInspector.PanelDescriptor} panelDescriptor
164 addPanel: function(panelDescriptor)
166 var weight = this._tabOrderSetting.get()[panelDescriptor.name()];
167 this._innerAddPanel(panelDescriptor, weight);
171 * @param {string} panelName
174 hasPanel: function(panelName)
176 return !!this._panelDescriptors[panelName];
180 * @param {string} panelName
181 * @return {!Promise.<!WebInspector.Panel>}
183 panel: function(panelName)
185 var panelDescriptor = this._panelDescriptors[panelName];
186 if (!panelDescriptor)
187 return Promise.reject(new Error("Can't load panel without the descriptor: " + panelName));
189 var promise = this._panelPromises[panelName];
193 promise = panelDescriptor.panel();
194 this._panelPromises[panelName] = promise;
196 promise.then(cachePanel.bind(this));
199 * @param {!WebInspector.Panel} panel
200 * @return {!WebInspector.Panel}
201 * @this {WebInspector.InspectorView}
203 function cachePanel(panel)
205 delete this._panelPromises[panelName];
206 this._panels[panelName] = panel;
213 * @param {!WebInspector.Event} event
215 _onSuspendStateChanged: function(event)
217 this._currentPanelLocked = WebInspector.targetManager.allTargetsSuspended();
218 this._tabbedPane.setCurrentTabLocked(this._currentPanelLocked);
219 if (this._leftToolbar)
220 this._leftToolbar.setEnabled(!this._currentPanelLocked);
221 if (this._rightToolbar)
222 this._rightToolbar.setEnabled(!this._currentPanelLocked);
226 * The returned Promise is resolved with null if another showPanel()
227 * gets called while this.panel(panelName) Promise is in flight.
229 * @param {string} panelName
230 * @return {!Promise.<?WebInspector.Panel>}
232 showPanel: function(panelName)
234 if (this._currentPanelLocked) {
235 if (this._currentPanel !== this._panels[panelName])
236 return Promise.reject(new Error("Current panel locked"));
237 return Promise.resolve(this._currentPanel);
240 this._panelForShowPromise = this.panel(panelName);
241 return this._panelForShowPromise.then(setCurrentPanelIfNecessary.bind(this, this._panelForShowPromise));
244 * @param {!Promise.<!WebInspector.Panel>} panelPromise
245 * @param {!WebInspector.Panel} panel
246 * @return {?WebInspector.Panel}
247 * @this {WebInspector.InspectorView}
249 function setCurrentPanelIfNecessary(panelPromise, panel)
251 if (this._panelForShowPromise !== panelPromise)
254 this.setCurrentPanel(panel);
260 * @param {string} panelName
261 * @param {string} iconType
262 * @param {string=} iconTooltip
264 setPanelIcon: function(panelName, iconType, iconTooltip)
266 this._tabbedPane.setTabIcon(panelName, iconType, iconTooltip);
270 * @return {!WebInspector.Panel}
272 currentPanel: function()
274 return this._currentPanel;
277 showInitialPanel: function()
279 if (InspectorFrontendHost.isUnderTest())
281 this._showInitialPanel();
284 _showInitialPanel: function()
286 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
288 this._drawer.initialPanelShown();
292 * @param {string} panelName
294 showInitialPanelForTest: function(panelName)
296 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
297 this.setCurrentPanel(this._panels[panelName]);
298 this._drawer.initialPanelShown();
301 _tabSelected: function()
303 var panelName = this._tabbedPane.selectedTabId;
307 this.showPanel(panelName);
311 * @param {!WebInspector.Panel} panel
312 * @param {boolean=} suppressBringToFront
313 * @return {!WebInspector.Panel}
315 setCurrentPanel: function(panel, suppressBringToFront)
317 delete this._panelForShowPromise;
319 if (this._currentPanelLocked) {
320 console.error("Current panel is locked");
321 return this._currentPanel;
324 if (!suppressBringToFront)
325 InspectorFrontendHost.bringToFront();
327 if (this._currentPanel === panel)
330 this._currentPanel = panel;
331 if (!this._panels[panel.name])
332 this._panels[panel.name] = panel;
333 this._tabbedPane.changeTabView(panel.name, panel);
334 this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
335 this._tabbedPane.selectTab(panel.name);
336 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
338 this._lastActivePanelSetting.set(panel.name);
339 this._pushToHistory(panel.name);
340 WebInspector.userMetrics.panelShown(panel.name);
348 closeViewInDrawer: function(id)
350 this._drawer.closeView(id);
355 * @param {string} title
356 * @param {!WebInspector.Widget} view
358 showCloseableViewInDrawer: function(id, title, view)
360 this._drawer.showCloseableView(id, title, view);
363 showDrawer: function()
365 this._drawer.showDrawer();
371 drawerVisible: function()
373 return this._drawer.isShowing();
378 * @param {boolean=} immediate
380 showViewInDrawer: function(id, immediate)
382 this._drawer.showView(id, immediate);
388 selectedViewInDrawer: function()
390 return this._drawer.selectedViewId();
393 closeDrawer: function()
395 this._drawer.closeDrawer();
402 defaultFocusedElement: function()
404 return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
407 _keyPress: function(event)
409 // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
410 // Any charCode < 32 is not going to be a valid keypress.
411 if (event.charCode < 32 && WebInspector.isWin())
413 clearTimeout(this._keyDownTimer);
414 delete this._keyDownTimer;
417 _keyDown: function(event)
419 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
422 var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
423 // Ctrl/Cmd + 1-9 should show corresponding panel.
424 var panelShortcutEnabled = WebInspector.moduleSetting("shortcutPanelSwitch").get();
425 if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
427 if (event.keyCode > 0x30 && event.keyCode < 0x3A)
428 panelIndex = event.keyCode - 0x31;
429 else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
430 panelIndex = event.keyCode - 0x61;
431 if (panelIndex !== -1) {
432 var panelName = this._tabbedPane.allTabs()[panelIndex];
434 if (!WebInspector.Dialog.currentInstance() && !this._currentPanelLocked)
435 this.showPanel(panelName);
442 // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
443 // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
444 // If there is, we cancel the timer and do not consider this a panel switch.
445 if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
446 this._keyDownInternal(event);
450 this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
453 _keyDownInternal: function(event)
455 if (this._currentPanelLocked)
460 if (this._openBracketIdentifiers[event.keyIdentifier])
463 if (this._closeBracketIdentifiers[event.keyIdentifier])
469 if (!event.shiftKey && !event.altKey) {
470 if (!WebInspector.Dialog.currentInstance())
471 this._changePanelInDirection(direction);
476 if (event.altKey && this._moveInHistory(direction))
481 * @param {number} direction
483 _changePanelInDirection: function(direction)
485 var panelOrder = this._tabbedPane.allTabs();
486 var index = panelOrder.indexOf(this.currentPanel().name);
487 index = (index + panelOrder.length + direction) % panelOrder.length;
488 this.showPanel(panelOrder[index]);
492 * @param {number} move
494 _moveInHistory: function(move)
496 var newIndex = this._historyIterator + move;
497 if (newIndex >= this._history.length || newIndex < 0)
500 this._inHistory = true;
501 this._historyIterator = newIndex;
502 if (!WebInspector.Dialog.currentInstance())
503 this.setCurrentPanel(this._panels[this._history[this._historyIterator]]);
504 delete this._inHistory;
509 _pushToHistory: function(panelName)
514 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
515 if (!this._history.length || this._history[this._history.length - 1] !== panelName)
516 this._history.push(panelName);
517 this._historyIterator = this._history.length - 1;
522 WebInspector.Dialog.modalHostRepositioned();
528 topResizerElement: function()
530 return this._tabbedPane.headerElement();
533 toolbarItemResized: function()
535 this._tabbedPane.headerResized();
539 * @param {!WebInspector.Event} event
541 _persistPanelOrder: function(event)
543 var tabs = /** @type {!Array.<!WebInspector.TabbedPaneTab>} */(event.data);
544 var tabOrders = this._tabOrderSetting.get();
545 for (var i = 0; i < tabs.length; i++)
546 tabOrders[tabs[i].id] = i;
547 this._tabOrderSetting.set(tabOrders);
550 __proto__: WebInspector.VBox.prototype
554 * @type {!WebInspector.InspectorView}
556 WebInspector.inspectorView;
560 * @implements {WebInspector.ActionDelegate}
562 WebInspector.InspectorView.DrawerToggleActionDelegate = function()
566 WebInspector.InspectorView.DrawerToggleActionDelegate.prototype = {
569 * @param {!WebInspector.Context} context
570 * @param {string} actionId
572 handleAction: function(context, actionId)
574 if (WebInspector.inspectorView.drawerVisible())
575 WebInspector.inspectorView.closeDrawer();
577 WebInspector.inspectorView.showDrawer();