Home | History | Annotate | Download | only in front_end
      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  */
     35 WebInspector.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 
     90 WebInspector.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  */
    486 WebInspector.inspectorView;
    487