Home | History | Annotate | Download | only in components
      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.push(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, self.runtime.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 toolbar-colors";
     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     this._toolbarItems = [];
     63 
     64     this._closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
     65     var closeButtonElement = this._closeButtonToolbarItem.createChild("div", "close-button");
     66     closeButtonElement.addEventListener("click", InspectorFrontendHost.closeWindow.bind(InspectorFrontendHost), true);
     67     this._rightToolbarElement.appendChild(this._closeButtonToolbarItem);
     68 
     69     this._panels = {};
     70     // Used by tests.
     71     WebInspector["panels"] = this._panels;
     72 
     73     this._history = [];
     74     this._historyIterator = -1;
     75     document.addEventListener("keydown", this._keyDown.bind(this), false);
     76     document.addEventListener("keypress", this._keyPress.bind(this), false);
     77     this._panelDescriptors = {};
     78 
     79     // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
     80     this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
     81     this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
     82     this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements");
     83 
     84     // FIXME(399531): enable timelineOnTraceEvents experiment when running layout tests under inspector/tracing/. This code
     85     // should be removed along with the old Timeline implementation once we move tracing based Timeline out of experimental.
     86     if ("tracing" === this._lastActivePanelSetting.get()) {
     87         Runtime.experiments.setEnabled("timelineOnTraceEvents", true);
     88         this._lastActivePanelSetting.set("timeline");
     89     }
     90 
     91     this._loadPanelDesciptors();
     92 
     93     InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ShowConsole, this.showPanel.bind(this, "console"));
     94 };
     95 
     96 WebInspector.InspectorView.prototype = {
     97     _loadPanelDesciptors: function()
     98     {
     99         WebInspector.startBatchUpdate();
    100         self.runtime.extensions(WebInspector.Panel).forEach(processPanelExtensions.bind(this));
    101         /**
    102          * @param {!Runtime.Extension} extension
    103          * @this {!WebInspector.InspectorView}
    104          */
    105         function processPanelExtensions(extension)
    106         {
    107             this.addPanel(new WebInspector.RuntimeExtensionPanelDescriptor(extension));
    108         }
    109         WebInspector.endBatchUpdate();
    110     },
    111 
    112     /**
    113      * @param {!WebInspector.StatusBarItem} item
    114      */
    115     appendToLeftToolbar: function(item)
    116     {
    117         this._toolbarItems.push(item);
    118         this._leftToolbarElement.appendChild(item.element);
    119     },
    120 
    121     /**
    122      * @param {!WebInspector.StatusBarItem} item
    123      */
    124     appendToRightToolbar: function(item)
    125     {
    126         this._toolbarItems.push(item);
    127         this._rightToolbarElement.insertBefore(item.element, this._closeButtonToolbarItem);
    128     },
    129 
    130     /**
    131      * @param {!WebInspector.PanelDescriptor} panelDescriptor
    132      */
    133     addPanel: function(panelDescriptor)
    134     {
    135         var panelName = panelDescriptor.name();
    136         this._panelDescriptors[panelName] = panelDescriptor;
    137         this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
    138         if (this._lastActivePanelSetting.get() === panelName)
    139             this._tabbedPane.selectTab(panelName);
    140     },
    141 
    142     /**
    143      * @param {string} panelName
    144      * @return {boolean}
    145      */
    146     hasPanel: function(panelName)
    147     {
    148         return !!this._panelDescriptors[panelName];
    149     },
    150 
    151     /**
    152      * @param {string} panelName
    153      * @return {?WebInspector.Panel}
    154      */
    155     panel: function(panelName)
    156     {
    157         var panelDescriptor = this._panelDescriptors[panelName];
    158         var panelOrder = this._tabbedPane.allTabs();
    159         if (!panelDescriptor && panelOrder.length)
    160             panelDescriptor = this._panelDescriptors[panelOrder[0]];
    161         var panel = panelDescriptor ? panelDescriptor.panel() : null;
    162         if (panel)
    163             this._panels[panelName] = panel;
    164         return panel;
    165     },
    166 
    167     /**
    168      * @param {boolean} locked
    169      */
    170     setCurrentPanelLocked: function(locked)
    171     {
    172         this._currentPanelLocked = locked;
    173         this._tabbedPane.setCurrentTabLocked(locked);
    174         for (var i = 0; i < this._toolbarItems.length; ++i)
    175             this._toolbarItems[i].setEnabled(!locked);
    176     },
    177 
    178     /**
    179      * @param {string} panelName
    180      * @return {?WebInspector.Panel}
    181      */
    182     showPanel: function(panelName)
    183     {
    184         if (this._currentPanelLocked)
    185             return this._currentPanel === this._panels[panelName] ? this._currentPanel : null;
    186 
    187         var panel = this.panel(panelName);
    188         if (panel)
    189             this.setCurrentPanel(panel);
    190         return panel;
    191     },
    192 
    193     /**
    194      * @return {!WebInspector.Panel}
    195      */
    196     currentPanel: function()
    197     {
    198         return this._currentPanel;
    199     },
    200 
    201     showInitialPanel: function()
    202     {
    203         this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
    204         this._tabSelected();
    205         this._drawer.initialPanelShown();
    206     },
    207 
    208     _tabSelected: function()
    209     {
    210         var panelName = this._tabbedPane.selectedTabId;
    211         if (!panelName)
    212             return;
    213         var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
    214         this._panels[panelName] = panel;
    215         this._tabbedPane.changeTabView(panelName, panel);
    216 
    217         this._currentPanel = panel;
    218         this._lastActivePanelSetting.set(panel.name);
    219         this._pushToHistory(panel.name);
    220         WebInspector.userMetrics.panelShown(panel.name);
    221         panel.focus();
    222     },
    223 
    224     /**
    225      * @param {!WebInspector.Panel} x
    226      */
    227     setCurrentPanel: function(x)
    228     {
    229         if (this._currentPanelLocked)
    230             return;
    231         InspectorFrontendHost.bringToFront();
    232         if (this._currentPanel === x)
    233             return;
    234 
    235         this._tabbedPane.changeTabView(x.name, x);
    236         this._tabbedPane.selectTab(x.name);
    237     },
    238 
    239     /**
    240      * @param {string} id
    241      */
    242     closeViewInDrawer: function(id)
    243     {
    244         this._drawer.closeView(id);
    245     },
    246 
    247     /**
    248      * @param {string} id
    249      * @param {string} title
    250      * @param {!WebInspector.View} view
    251      */
    252     showCloseableViewInDrawer: function(id, title, view)
    253     {
    254         this._drawer.showCloseableView(id, title, view);
    255     },
    256 
    257     showDrawer: function()
    258     {
    259         this._drawer.showDrawer();
    260     },
    261 
    262     /**
    263      * @return {boolean}
    264      */
    265     drawerVisible: function()
    266     {
    267         return this._drawer.isShowing();
    268     },
    269 
    270     /**
    271      * @param {string} id
    272      * @param {boolean=} immediate
    273      */
    274     showViewInDrawer: function(id, immediate)
    275     {
    276         this._drawer.showView(id, immediate);
    277     },
    278 
    279     /**
    280      * @return {?string}
    281      */
    282     selectedViewInDrawer: function()
    283     {
    284         return this._drawer.selectedViewId();
    285     },
    286 
    287     closeDrawer: function()
    288     {
    289         this._drawer.closeDrawer();
    290     },
    291 
    292     /**
    293      * @return {!Element}
    294      */
    295     defaultFocusedElement: function()
    296     {
    297         return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
    298     },
    299 
    300     _keyPress: function(event)
    301     {
    302         // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
    303         // Any charCode < 32 is not going to be a valid keypress.
    304         if (event.charCode < 32 && WebInspector.isWin())
    305             return;
    306         clearTimeout(this._keyDownTimer);
    307         delete this._keyDownTimer;
    308     },
    309 
    310     _keyDown: function(event)
    311     {
    312         if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
    313             return;
    314 
    315         var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
    316         // Ctrl/Cmd + 1-9 should show corresponding panel.
    317         var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
    318         if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
    319             var panelIndex = -1;
    320             if (event.keyCode > 0x30 && event.keyCode < 0x3A)
    321                 panelIndex = event.keyCode - 0x31;
    322             else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
    323                 panelIndex = event.keyCode - 0x61;
    324             if (panelIndex !== -1) {
    325                 var panelName = this._tabbedPane.allTabs()[panelIndex];
    326                 if (panelName) {
    327                     if (!WebInspector.Dialog.currentInstance() && !this._currentPanelLocked)
    328                         this.showPanel(panelName);
    329                     event.consume(true);
    330                 }
    331                 return;
    332             }
    333         }
    334 
    335         // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
    336         // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
    337         // If there is, we cancel the timer and do not consider this a panel switch.
    338         if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
    339             this._keyDownInternal(event);
    340             return;
    341         }
    342 
    343         this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
    344     },
    345 
    346     _keyDownInternal: function(event)
    347     {
    348         if (this._currentPanelLocked)
    349             return;
    350 
    351         var direction = 0;
    352 
    353         if (this._openBracketIdentifiers[event.keyIdentifier])
    354             direction = -1;
    355 
    356         if (this._closeBracketIdentifiers[event.keyIdentifier])
    357             direction = 1;
    358 
    359         if (!direction)
    360             return;
    361 
    362         if (!event.shiftKey && !event.altKey) {
    363             if (!WebInspector.Dialog.currentInstance())
    364                 this._changePanelInDirection(direction);
    365             event.consume(true);
    366             return;
    367         }
    368 
    369         if (event.altKey && this._moveInHistory(direction))
    370             event.consume(true)
    371     },
    372 
    373     _changePanelInDirection: function(direction)
    374     {
    375         var panelOrder = this._tabbedPane.allTabs();
    376         var index = panelOrder.indexOf(this.currentPanel().name);
    377         index = (index + panelOrder.length + direction) % panelOrder.length;
    378         this.showPanel(panelOrder[index]);
    379     },
    380 
    381     _moveInHistory: function(move)
    382     {
    383         var newIndex = this._historyIterator + move;
    384         if (newIndex >= this._history.length || newIndex < 0)
    385             return false;
    386 
    387         this._inHistory = true;
    388         this._historyIterator = newIndex;
    389         if (!WebInspector.Dialog.currentInstance())
    390             this.setCurrentPanel(this._panels[this._history[this._historyIterator]]);
    391         delete this._inHistory;
    392 
    393         return true;
    394     },
    395 
    396     _pushToHistory: function(panelName)
    397     {
    398         if (this._inHistory)
    399             return;
    400 
    401         this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
    402         if (!this._history.length || this._history[this._history.length - 1] !== panelName)
    403             this._history.push(panelName);
    404         this._historyIterator = this._history.length - 1;
    405     },
    406 
    407     onResize: function()
    408     {
    409         WebInspector.Dialog.modalHostRepositioned();
    410     },
    411 
    412     /**
    413      * @return {!Element}
    414      */
    415     topResizerElement: function()
    416     {
    417         return this._tabbedPane.headerElement();
    418     },
    419 
    420     toolbarItemResized: function()
    421     {
    422         this._tabbedPane.headerResized();
    423     },
    424 
    425     __proto__: WebInspector.VBox.prototype
    426 };
    427 
    428 /**
    429  * @type {!WebInspector.InspectorView}
    430  */
    431 WebInspector.inspectorView;
    432 
    433 /**
    434  * @constructor
    435  * @implements {WebInspector.ActionDelegate}
    436  */
    437 WebInspector.InspectorView.DrawerToggleActionDelegate = function()
    438 {
    439 }
    440 
    441 WebInspector.InspectorView.DrawerToggleActionDelegate.prototype = {
    442     /**
    443      * @return {boolean}
    444      */
    445     handleAction: function()
    446     {
    447         if (WebInspector.inspectorView.drawerVisible()) {
    448             WebInspector.inspectorView.closeDrawer();
    449             return true;
    450         }
    451         WebInspector.inspectorView.showDrawer();
    452         return true;
    453     }
    454 }
    455 
    456 /**
    457  * @constructor
    458  * @implements {WebInspector.StatusBarItem.Provider}
    459  */
    460 WebInspector.InspectorView.ToggleDrawerButtonProvider = function()
    461 {
    462 }
    463 
    464 WebInspector.InspectorView.ToggleDrawerButtonProvider.prototype = {
    465     /**
    466      * @return {?WebInspector.StatusBarItem}
    467      */
    468     item: function()
    469     {
    470         return WebInspector.inspectorView._drawer.toggleButton();
    471     }
    472 }
    473