• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.View}
34 */
35WebInspector.InspectorView = function()
36{
37    WebInspector.View.call(this);
38    this.markAsRoot();
39    this.element.classList.add("fill", "vbox", "inspector-view");
40    this.element.setAttribute("spellcheck", false);
41
42    this._splitView = new WebInspector.SplitView(false, "InspectorView.splitView", 300, 300);
43    this._splitView.setSecondIsSidebar(true);
44    this._splitView.setSidebarElementConstraints(150, 50);
45    this._splitView.setMainElementConstraints(50, 50);
46    WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._updateSplitView.bind(this));
47
48    this._splitView.element.id = "inspector-split-view";
49    this._splitView.show(this.element);
50
51    this._overlayView = new WebInspector.ViewWithResizeCallback(this._onOverlayResized.bind(this));
52    this._overlayView.show(this._splitView.mainElement);
53
54    this._devtoolsElement = this._splitView.sidebarElement;
55    this._devtoolsElement.classList.add("vbox");
56
57    this._tabbedPane = new WebInspector.TabbedPane();
58    this._tabbedPane.setRetainTabsOrder(true);
59    this._tabbedPane.show(this._devtoolsElement);
60
61    this._toolbarElement = document.createElement("div");
62    this._toolbarElement.className = "toolbar toolbar-background";
63    var headerElement = this._tabbedPane.headerElement();
64    headerElement.parentElement.insertBefore(this._toolbarElement, headerElement);
65
66    this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left");
67    this._toolbarElement.appendChild(headerElement);
68    this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right");
69
70    this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden");
71    this._errorWarningCountElement.id = "error-warning-count";
72
73    this._drawer = new WebInspector.Drawer(this);
74    this.appendToRightToolbar(this._drawer.toggleButtonElement());
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._updateSplitView();
88}
89
90WebInspector.InspectorView.prototype = {
91    /**
92     * @param {!Element} element
93     */
94    appendToLeftToolbar: function(element)
95    {
96        this._leftToolbarElement.appendChild(element);
97    },
98
99    /**
100     * @param {!Element} element
101     */
102    appendToRightToolbar: function(element)
103    {
104        this._rightToolbarElement.appendChild(element);
105    },
106
107    /**
108     * @return {!WebInspector.Drawer}
109     */
110    drawer: function()
111    {
112        return this._drawer;
113    },
114
115    /**
116     * @return {!Element}
117     */
118    devtoolsElement: function()
119    {
120        return this._devtoolsElement;
121    },
122
123    /**
124     * @param {!WebInspector.PanelDescriptor} panelDescriptor
125     */
126    addPanel: function(panelDescriptor)
127    {
128        var panelName = panelDescriptor.name();
129        this._panelDescriptors[panelName] = panelDescriptor;
130        this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
131        if (this._lastActivePanelSetting.get() === panelName)
132            this._tabbedPane.selectTab(panelName);
133    },
134
135    /**
136     * @param {string} panelName
137     * @return {?WebInspector.Panel}
138     */
139    panel: function(panelName)
140    {
141        var panelDescriptor = this._panelDescriptors[panelName];
142        var panelOrder = this._tabbedPane.allTabs();
143        if (!panelDescriptor && panelOrder.length)
144            panelDescriptor = this._panelDescriptors[panelOrder[0]];
145        return panelDescriptor ? panelDescriptor.panel() : null;
146    },
147
148    /**
149     * @param {string} panelName
150     * @return {?WebInspector.Panel}
151     */
152    showPanel: function(panelName)
153    {
154        var panel = this.panel(panelName);
155        if (panel)
156            this.setCurrentPanel(panel);
157        return panel;
158    },
159
160    /**
161     * @return {!WebInspector.Panel}
162     */
163    currentPanel: function()
164    {
165        return this._currentPanel;
166    },
167
168    showInitialPanel: function()
169    {
170        this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
171        this._tabSelected();
172        this._drawer.showOnLoadIfNecessary();
173    },
174
175    _tabSelected: function()
176    {
177        var panelName = this._tabbedPane.selectedTabId;
178        var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
179        this._tabbedPane.changeTabView(panelName, panel);
180
181        this._currentPanel = panel;
182        this._lastActivePanelSetting.set(panel.name);
183        this._pushToHistory(panel.name);
184        WebInspector.userMetrics.panelShown(panel.name);
185        panel.focus();
186    },
187
188    /**
189     * @param {!WebInspector.Panel} x
190     */
191    setCurrentPanel: function(x)
192    {
193        if (this._currentPanel === x)
194            return;
195
196        this._tabbedPane.changeTabView(x.name, x);
197        this._tabbedPane.selectTab(x.name);
198    },
199
200    /**
201     * @param {string} id
202     */
203    closeViewInDrawer: function(id)
204    {
205        return this._drawer.closeView(id);
206    },
207
208    /**
209     * @param {string} id
210     * @param {string} title
211     * @param {!WebInspector.View} view
212     */
213    showCloseableViewInDrawer: function(id, title, view)
214    {
215        this._drawer.showCloseableView(id, title, view);
216    },
217
218    /**
219     * @param {string} id
220     * @param {string} title
221     * @param {!WebInspector.ViewFactory} factory
222     */
223    registerViewInDrawer: function(id, title, factory)
224    {
225        this._drawer.registerView(id, title, factory);
226    },
227
228    /**
229     * @param {string} id
230     */
231    unregisterViewInDrawer: function(id)
232    {
233        this._drawer.unregisterView(id);
234    },
235
236    /**
237     * @param {string} id
238     */
239    showViewInDrawer: function(id)
240    {
241        this._drawer.showView(id);
242    },
243
244    /**
245     * @return {string}
246     */
247    selectedViewInDrawer: function()
248    {
249        return this._drawer.selectedViewId();
250    },
251
252    closeDrawer: function()
253    {
254        this._drawer.hide();
255    },
256
257    /**
258     * @return {!Element}
259     */
260    defaultFocusedElement: function()
261    {
262        return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
263    },
264
265    _keyPress: function(event)
266    {
267        // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
268        // Any charCode < 32 is not going to be a valid keypress.
269        if (event.charCode < 32 && WebInspector.isWin())
270            return;
271        clearTimeout(this._keyDownTimer);
272        delete this._keyDownTimer;
273    },
274
275    _keyDown: function(event)
276    {
277        if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
278            return;
279
280        var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
281        // Ctrl/Cmd + 1-9 should show corresponding panel.
282        var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
283        if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
284            var panelIndex = -1;
285            if (event.keyCode > 0x30 && event.keyCode < 0x3A)
286                panelIndex = event.keyCode - 0x31;
287            else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
288                panelIndex = event.keyCode - 0x61;
289            if (panelIndex !== -1) {
290                var panelName = this._tabbedPane.allTabs()[panelIndex];
291                if (panelName) {
292                    this.showPanel(panelName);
293                    event.consume(true);
294                }
295                return;
296            }
297        }
298
299        // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
300        // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
301        // If there is, we cancel the timer and do not consider this a panel switch.
302        if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
303            this._keyDownInternal(event);
304            return;
305        }
306
307        this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
308    },
309
310    _keyDownInternal: function(event)
311    {
312        if (this._openBracketIdentifiers[event.keyIdentifier]) {
313            var isRotateLeft = !event.shiftKey && !event.altKey;
314            if (isRotateLeft) {
315                var panelOrder = this._tabbedPane.allTabs();
316                var index = panelOrder.indexOf(this.currentPanel().name);
317                index = (index === 0) ? panelOrder.length - 1 : index - 1;
318                this.showPanel(panelOrder[index]);
319                event.consume(true);
320                return;
321            }
322
323            var isGoBack = event.altKey;
324            if (isGoBack && this._canGoBackInHistory()) {
325                this._goBackInHistory();
326                event.consume(true);
327            }
328            return;
329        }
330
331        if (this._closeBracketIdentifiers[event.keyIdentifier]) {
332            var isRotateRight = !event.shiftKey && !event.altKey;
333            if (isRotateRight) {
334                var panelOrder = this._tabbedPane.allTabs();
335                var index = panelOrder.indexOf(this.currentPanel().name);
336                index = (index + 1) % panelOrder.length;
337                this.showPanel(panelOrder[index]);
338                event.consume(true);
339                return;
340            }
341
342            var isGoForward = event.altKey;
343            if (isGoForward && this._canGoForwardInHistory()) {
344                this._goForwardInHistory();
345                event.consume(true);
346            }
347            return;
348        }
349    },
350
351    _canGoBackInHistory: function()
352    {
353        return this._historyIterator > 0;
354    },
355
356    _goBackInHistory: function()
357    {
358        this._inHistory = true;
359        this.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]);
360        delete this._inHistory;
361    },
362
363    _canGoForwardInHistory: function()
364    {
365        return this._historyIterator < this._history.length - 1;
366    },
367
368    _goForwardInHistory: function()
369    {
370        this._inHistory = true;
371        this.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]);
372        delete this._inHistory;
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        // FIXME: make drawer a view.
389        this.doResize();
390        this._drawer.resize();
391    },
392
393    _updateSplitView: function()
394    {
395        var dockSide = WebInspector.dockController.dockSide();
396        if (WebInspector.queryParamsObject["overlayContents"] && dockSide !== WebInspector.DockController.State.Undocked) {
397            this._splitView.showBoth();
398            var vertical = dockSide === WebInspector.DockController.State.DockedToRight;
399            this._splitView.setVertical(vertical);
400            if (vertical) {
401                this._splitView.uninstallResizer(this._tabbedPane.headerElement());
402                this._splitView.installResizer(this._splitView.resizerElement());
403            } else {
404                this._splitView.uninstallResizer(this._splitView.resizerElement());
405                this._splitView.installResizer(this._tabbedPane.headerElement());
406            }
407        } else {
408            this._splitView.showOnlySecond();
409        }
410    },
411
412    _onOverlayResized: function()
413    {
414        var dockSide = WebInspector.dockController.dockSide();
415        if (WebInspector.queryParamsObject["overlayContents"] && dockSide !== WebInspector.DockController.State.Undocked) {
416            // Leave 3px room for resizer.
417            var bottom = this._splitView.isVertical() ? 0 : this._splitView.sidebarSize();
418            var right = this._splitView.isVertical() ? this._splitView.sidebarSize() + 3 : 0;
419            InspectorFrontendHost.setContentsInsets(0, 0, bottom, right);
420        }
421
422        // FIXME: make drawer a view.
423        this._drawer.resize();
424    },
425
426    /**
427     * @param {number} errors
428     * @param {number} warnings
429     */
430    setErrorAndWarningCounts: function(errors, warnings)
431    {
432        if (!errors && !warnings) {
433            this._errorWarningCountElement.classList.add("hidden");
434            this._tabbedPane.headerResized();
435            return;
436        }
437
438        this._errorWarningCountElement.classList.remove("hidden");
439        this._errorWarningCountElement.removeChildren();
440
441        if (errors) {
442            var errorImageElement = this._errorWarningCountElement.createChild("div", "error-icon-small");
443            var errorElement = this._errorWarningCountElement.createChild("span");
444            errorElement.id = "error-count";
445            errorElement.textContent = errors;
446        }
447
448        if (warnings) {
449            var warningsImageElement = this._errorWarningCountElement.createChild("div", "warning-icon-small");
450            var warningsElement = this._errorWarningCountElement.createChild("span");
451            warningsElement.id = "warning-count";
452            warningsElement.textContent = warnings;
453        }
454
455        if (errors) {
456            if (warnings) {
457                if (errors == 1) {
458                    if (warnings == 1)
459                        this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
460                    else
461                        this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
462                } else if (warnings == 1)
463                    this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
464                else
465                    this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
466            } else if (errors == 1)
467                this._errorWarningCountElement.title = WebInspector.UIString("%d error", errors);
468            else
469                this._errorWarningCountElement.title = WebInspector.UIString("%d errors", errors);
470        } else if (warnings == 1)
471            this._errorWarningCountElement.title = WebInspector.UIString("%d warning", warnings);
472        else if (warnings)
473            this._errorWarningCountElement.title = WebInspector.UIString("%d warnings", warnings);
474        else
475            this._errorWarningCountElement.title = null;
476
477        this._tabbedPane.headerResized();
478    },
479
480    __proto__: WebInspector.View.prototype
481};
482
483/**
484 * @type {!WebInspector.InspectorView}
485 */
486WebInspector.inspectorView;
487