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.View}
35 WebInspector.InspectorView = function()
37 WebInspector.View.call(this);
39 this.element.classList.add("fill", "inspector-view");
40 this.element.setAttribute("spellcheck", false);
42 window.addEventListener("resize", this._onWindowResize.bind(this), true);
43 WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
45 // We can use split view either for docking or screencast, but not together.
46 var settingName = WebInspector.queryParamsObject["can_dock"] ? "InspectorView.splitView" : "InspectorView.screencastSplitView";
47 this._splitView = new WebInspector.SplitView(false, true, settingName, 300, 300);
48 this._updateConstraints();
49 WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._updateSplitView.bind(this));
51 this._splitView.element.id = "inspector-split-view";
52 this._splitView.show(this.element);
54 // Main part of main split is overlay view.
55 this._overlayView = new WebInspector.InspectorView.OverlayView(this._splitView);
56 this._overlayView.show(this._splitView.mainElement());
58 // Sidebar of main split is artificial element used for positioning.
59 this._devtoolsView = new WebInspector.ViewWithResizeCallback(this._onDevToolsViewResized.bind(this));
60 this._devtoolsView.show(this._splitView.sidebarElement());
61 WebInspector.Dialog.setModalHostView(this._devtoolsView);
63 // DevTools sidebar is a vertical split of panels tabbed pane and a drawer.
64 this._drawerSplitView = new WebInspector.SplitView(false, true, "Inspector.drawerSplitView", 200, 200);
65 this._drawerSplitView.setSidebarElementConstraints(Preferences.minDrawerHeight, Preferences.minDrawerHeight);
66 this._drawerSplitView.setMainElementConstraints(25, 25);
67 this._drawerSplitView.show(this._devtoolsView.element);
69 this._tabbedPane = new WebInspector.TabbedPane();
70 this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(WebInspector.Panel, "name", "order"));
71 this._tabbedPane.show(this._drawerSplitView.mainElement());
72 this._drawer = new WebInspector.Drawer(this._drawerSplitView);
74 // Patch tabbed pane header with toolbar actions.
75 this._toolbarElement = document.createElement("div");
76 this._toolbarElement.className = "toolbar toolbar-background";
77 var headerElement = this._tabbedPane.headerElement();
78 headerElement.parentElement.insertBefore(this._toolbarElement, headerElement);
80 this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left");
81 this._toolbarElement.appendChild(headerElement);
82 this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right");
84 this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden");
85 this._errorWarningCountElement.id = "error-warning-count";
87 this._closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
88 var closeButtonElement = this._closeButtonToolbarItem.createChild("div", "close-button");
89 closeButtonElement.addEventListener("click", WebInspector.close.bind(WebInspector), true);
90 this._rightToolbarElement.appendChild(this._closeButtonToolbarItem);
92 this.appendToRightToolbar(this._drawer.toggleButtonElement());
95 this._historyIterator = -1;
96 document.addEventListener("keydown", this._keyDown.bind(this), false);
97 document.addEventListener("keypress", this._keyPress.bind(this), false);
98 this._panelDescriptors = {};
100 // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
101 this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
102 this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
103 this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements");
105 this._updateSplitView();
107 this._loadPanelDesciptors();
110 WebInspector.InspectorView.Constraints = {
117 WebInspector.InspectorView.prototype = {
118 _loadPanelDesciptors: function()
120 WebInspector.startBatchUpdate();
121 WebInspector.moduleManager.extensions(WebInspector.Panel).forEach(processPanelExtensions.bind(this));
123 * @param {!WebInspector.ModuleManager.Extension} extension
124 * @this {!WebInspector.InspectorView}
126 function processPanelExtensions(extension)
128 this.addPanel(new WebInspector.ModuleManagerExtensionPanelDescriptor(extension));
130 WebInspector.endBatchUpdate();
134 * @param {!Element} element
136 appendToLeftToolbar: function(element)
138 this._leftToolbarElement.appendChild(element);
142 * @param {!Element} element
144 appendToRightToolbar: function(element)
146 this._rightToolbarElement.insertBefore(element, this._closeButtonToolbarItem);
152 devtoolsElement: function()
154 return this._devtoolsView.element;
158 * @param {!WebInspector.PanelDescriptor} panelDescriptor
160 addPanel: function(panelDescriptor)
162 var panelName = panelDescriptor.name();
163 this._panelDescriptors[panelName] = panelDescriptor;
164 this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
165 if (this._lastActivePanelSetting.get() === panelName)
166 this._tabbedPane.selectTab(panelName);
170 * @param {string} panelName
171 * @return {?WebInspector.Panel}
173 panel: function(panelName)
175 var panelDescriptor = this._panelDescriptors[panelName];
176 var panelOrder = this._tabbedPane.allTabs();
177 if (!panelDescriptor && panelOrder.length)
178 panelDescriptor = this._panelDescriptors[panelOrder[0]];
179 return panelDescriptor ? panelDescriptor.panel() : null;
183 * @param {string} panelName
184 * @return {?WebInspector.Panel}
186 showPanel: function(panelName)
188 var panel = this.panel(panelName);
190 this.setCurrentPanel(panel);
195 * @return {!WebInspector.Panel}
197 currentPanel: function()
199 return this._currentPanel;
202 showInitialPanel: function()
204 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
206 this._drawer.showOnLoadIfNecessary();
209 _tabSelected: function()
211 var panelName = this._tabbedPane.selectedTabId;
212 var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
213 this._tabbedPane.changeTabView(panelName, panel);
215 this._currentPanel = panel;
216 this._lastActivePanelSetting.set(panel.name);
217 this._pushToHistory(panel.name);
218 WebInspector.userMetrics.panelShown(panel.name);
223 * @param {!WebInspector.Panel} x
225 setCurrentPanel: function(x)
227 if (this._currentPanel === x)
230 this._tabbedPane.changeTabView(x.name, x);
231 this._tabbedPane.selectTab(x.name);
237 closeViewInDrawer: function(id)
239 this._drawer.closeView(id);
244 * @param {string} title
245 * @param {!WebInspector.View} view
247 showCloseableViewInDrawer: function(id, title, view)
249 this._drawer.showCloseableView(id, title, view);
252 showDrawer: function()
254 this._drawer.showDrawer();
260 drawerVisible: function()
262 return this._drawer.isShowing();
267 * @param {boolean=} immediate
269 showViewInDrawer: function(id, immediate)
271 this._drawer.showView(id, immediate);
277 selectedViewInDrawer: function()
279 return this._drawer.selectedViewId();
282 closeDrawer: function()
284 this._drawer.closeDrawer();
290 defaultFocusedElement: function()
292 return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
295 _keyPress: function(event)
297 // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
298 // Any charCode < 32 is not going to be a valid keypress.
299 if (event.charCode < 32 && WebInspector.isWin())
301 clearTimeout(this._keyDownTimer);
302 delete this._keyDownTimer;
305 _keyDown: function(event)
307 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
310 var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
311 // Ctrl/Cmd + 1-9 should show corresponding panel.
312 var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
313 if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
315 if (event.keyCode > 0x30 && event.keyCode < 0x3A)
316 panelIndex = event.keyCode - 0x31;
317 else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
318 panelIndex = event.keyCode - 0x61;
319 if (panelIndex !== -1) {
320 var panelName = this._tabbedPane.allTabs()[panelIndex];
322 this.showPanel(panelName);
329 // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
330 // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
331 // If there is, we cancel the timer and do not consider this a panel switch.
332 if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
333 this._keyDownInternal(event);
337 this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
340 _keyDownInternal: function(event)
342 if (this._openBracketIdentifiers[event.keyIdentifier]) {
343 var isRotateLeft = !event.shiftKey && !event.altKey;
345 var panelOrder = this._tabbedPane.allTabs();
346 var index = panelOrder.indexOf(this.currentPanel().name);
347 index = (index === 0) ? panelOrder.length - 1 : index - 1;
348 this.showPanel(panelOrder[index]);
353 var isGoBack = event.altKey;
354 if (isGoBack && this._canGoBackInHistory()) {
355 this._goBackInHistory();
361 if (this._closeBracketIdentifiers[event.keyIdentifier]) {
362 var isRotateRight = !event.shiftKey && !event.altKey;
364 var panelOrder = this._tabbedPane.allTabs();
365 var index = panelOrder.indexOf(this.currentPanel().name);
366 index = (index + 1) % panelOrder.length;
367 this.showPanel(panelOrder[index]);
372 var isGoForward = event.altKey;
373 if (isGoForward && this._canGoForwardInHistory()) {
374 this._goForwardInHistory();
381 _canGoBackInHistory: function()
383 return this._historyIterator > 0;
386 _goBackInHistory: function()
388 this._inHistory = true;
389 this.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]);
390 delete this._inHistory;
393 _canGoForwardInHistory: function()
395 return this._historyIterator < this._history.length - 1;
398 _goForwardInHistory: function()
400 this._inHistory = true;
401 this.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]);
402 delete this._inHistory;
405 _pushToHistory: function(panelName)
410 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
411 if (!this._history.length || this._history[this._history.length - 1] !== panelName)
412 this._history.push(panelName);
413 this._historyIterator = this._history.length - 1;
416 _onDevToolsViewResized: function()
418 WebInspector.Dialog.modalHostRepositioned();
421 _onWindowResize: function()
426 _updateSplitView: function()
428 var dockSide = WebInspector.dockController.dockSide();
429 if (dockSide !== WebInspector.DockController.State.Undocked) {
430 var vertical = WebInspector.dockController.isVertical();
431 this._splitView.setVertical(vertical);
434 if (dockSide === WebInspector.DockController.State.DockedToRight)
435 this._overlayView.setMargins(false, true, false, false);
437 this._overlayView.setMargins(false, false, false, true);
438 this._splitView.setSecondIsSidebar(dockSide === WebInspector.DockController.State.DockedToRight);
439 this._splitView.uninstallResizer(this._tabbedPane.headerElement());
440 this._splitView.installResizer(this._splitView.resizerElement());
443 this._overlayView.setMargins(false, false, false, false);
444 this._splitView.setSecondIsSidebar(true);
445 this._splitView.installResizer(this._splitView.resizerElement());
446 this._splitView.installResizer(this._tabbedPane.headerElement());
448 this._splitView.showBoth();
450 this._overlayView.setMargins(false, false, false, false);
451 this._splitView.hideMain();
452 this._splitView.uninstallResizer(this._tabbedPane.headerElement());
453 this._splitView.uninstallResizer(this._splitView.resizerElement());
457 _onZoomChanged: function(event)
459 this._updateConstraints();
460 var data = /** @type {{from: number, to: number}} */ (event.data);
461 this._splitView.setSidebarSize(this._splitView.sidebarSize() * data.from / data.to, true);
462 this._overlayView.updateMargins();
465 _updateConstraints: function()
467 var zoomFactor = WebInspector.zoomManager.zoomFactor();
468 this._splitView.setSidebarElementConstraints(WebInspector.InspectorView.Constraints.DevToolsWidth / zoomFactor,
469 WebInspector.InspectorView.Constraints.DevToolsHeight / zoomFactor);
470 this._splitView.setMainElementConstraints(WebInspector.InspectorView.Constraints.OverlayWidth / zoomFactor,
471 WebInspector.InspectorView.Constraints.OverlayHeight / zoomFactor);
475 * @param {!WebInspector.View} view
476 * @param {boolean} vertical
478 showScreencastView: function(view, vertical)
480 if (view.parentView() !== this._overlayView)
481 view.show(this._overlayView.element);
482 this._splitView.setVertical(vertical);
483 this._splitView.installResizer(this._splitView.resizerElement());
484 this._splitView.showBoth();
487 hideScreencastView: function()
489 this._splitView.hideMain();
493 * @param {number} errors
494 * @param {number} warnings
496 setErrorAndWarningCounts: function(errors, warnings)
498 if (!errors && !warnings) {
499 this._errorWarningCountElement.classList.add("hidden");
500 this._tabbedPane.headerResized();
504 this._errorWarningCountElement.classList.remove("hidden");
505 this._errorWarningCountElement.removeChildren();
508 var errorImageElement = this._errorWarningCountElement.createChild("div", "error-icon-small");
509 var errorElement = this._errorWarningCountElement.createChild("span");
510 errorElement.id = "error-count";
511 errorElement.textContent = errors;
515 var warningsImageElement = this._errorWarningCountElement.createChild("div", "warning-icon-small");
516 var warningsElement = this._errorWarningCountElement.createChild("span");
517 warningsElement.id = "warning-count";
518 warningsElement.textContent = warnings;
525 this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
527 this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
528 } else if (warnings == 1)
529 this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
531 this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
532 } else if (errors == 1)
533 this._errorWarningCountElement.title = WebInspector.UIString("%d error", errors);
535 this._errorWarningCountElement.title = WebInspector.UIString("%d errors", errors);
536 } else if (warnings == 1)
537 this._errorWarningCountElement.title = WebInspector.UIString("%d warning", warnings);
539 this._errorWarningCountElement.title = WebInspector.UIString("%d warnings", warnings);
541 this._errorWarningCountElement.title = null;
543 this._tabbedPane.headerResized();
546 __proto__: WebInspector.View.prototype
551 * @param {!WebInspector.SplitView} splitView
552 * @extends {WebInspector.View}
554 WebInspector.InspectorView.OverlayView = function(splitView)
556 WebInspector.View.call(this);
557 this._margins = {top: 0, left: 0, right: 0, bottom: 0};
558 this._splitView = splitView;
561 WebInspector.InspectorView.OverlayView.prototype = {
563 * @param {boolean} top
564 * @param {boolean} right
565 * @param {boolean} bottom
566 * @param {boolean} left
568 setMargins: function(top, right, bottom, left)
570 this._margins = { top: top, right: right, bottom: bottom, left: left };
571 this.updateMargins();
574 updateMargins: function()
576 var marginValue = Math.round(3 / WebInspector.zoomManager.zoomFactor()) + "px ";
577 var margins = this._margins.top ? marginValue : "0 ";
578 margins += this._margins.right ? marginValue : "0 ";
579 margins += this._margins.bottom ? marginValue : "0 ";
580 margins += this._margins.left ? marginValue : "0 ";
581 this.element.style.margin = margins;
586 var dockSide = WebInspector.dockController.dockSide();
587 if (dockSide !== WebInspector.DockController.State.Undocked) {
588 if (this._setContentsInsetsId)
589 window.cancelAnimationFrame(this._setContentsInsetsId);
590 this._setContentsInsetsId = window.requestAnimationFrame(this._setContentsInsets.bind(this));
594 _setContentsInsets: function()
596 delete this._setContentsInsetsId;
598 var zoomFactor = WebInspector.zoomManager.zoomFactor();
599 var marginValue = Math.round(3 / zoomFactor);
601 top: this._margins.top ? marginValue : 0,
602 left: this._margins.left ? marginValue : 0,
603 right: this._margins.right ? marginValue : 0,
604 bottom: this._margins.bottom ? marginValue : 0};
607 width: WebInspector.InspectorView.Constraints.OverlayWidth - Math.round(insets.left * zoomFactor) - Math.round(insets.right * zoomFactor),
608 height: WebInspector.InspectorView.Constraints.OverlayHeight - Math.round(insets.top * zoomFactor) - Math.round(insets.bottom * zoomFactor)};
610 insets[this._splitView.sidebarSide()] += this._splitView.desiredSidebarSize();
613 top: Math.round(insets.top * zoomFactor),
614 left: Math.round(insets.left * zoomFactor),
615 bottom: Math.round(insets.bottom * zoomFactor),
616 right: Math.round(insets.right * zoomFactor)};
618 InspectorFrontendHost.setContentsResizingStrategy(zoomedInsets, minSize);
621 __proto__: WebInspector.View.prototype
625 * @type {!WebInspector.InspectorView}
627 WebInspector.inspectorView;