Home | History | Annotate | Download | only in console
      1 /*
      2  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
      3  * Copyright (C) 2009 Joseph Pecoraro
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 /**
     31  * @constructor
     32  * @extends {WebInspector.VBox}
     33  * @implements {WebInspector.Searchable}
     34  * @implements {WebInspector.TargetManager.Observer}
     35  * @implements {WebInspector.ViewportControl.Provider}
     36  * @param {boolean} hideContextSelector
     37  */
     38 WebInspector.ConsoleView = function(hideContextSelector)
     39 {
     40     WebInspector.VBox.call(this);
     41     this.registerRequiredCSS("filter.css");
     42 
     43     this._searchableView = new WebInspector.SearchableView(this);
     44     this._searchableView.setMinimalSearchQuerySize(0);
     45     this._searchableView.show(this.element);
     46 
     47     this._contentsElement = this._searchableView.element;
     48     this._contentsElement.classList.add("console-view");
     49     this._visibleViewMessages = [];
     50     this._urlToMessageCount = {};
     51     this._hiddenByFilterCount = 0;
     52 
     53     this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item");
     54     this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this);
     55 
     56     this._executionContextSelector = new WebInspector.StatusBarComboBox(this._executionContextChanged.bind(this), "console-context");
     57 
     58     /**
     59      * @type {!Map.<!WebInspector.ExecutionContext, !Element>}
     60      */
     61     this._optionByExecutionContext = new Map();
     62 
     63     this._filter = new WebInspector.ConsoleViewFilter(this);
     64     this._filter.addEventListener(WebInspector.ConsoleViewFilter.Events.FilterChanged, this._updateMessageList.bind(this));
     65 
     66     if (hideContextSelector)
     67         this._executionContextSelector.element.classList.add("hidden");
     68 
     69     this._filterBar = new WebInspector.FilterBar();
     70 
     71     var statusBarElement = this._contentsElement.createChild("div", "console-status-bar");
     72     statusBarElement.appendChild(this._clearConsoleButton.element);
     73     statusBarElement.appendChild(this._filterBar.filterButton().element);
     74     statusBarElement.appendChild(this._executionContextSelector.element);
     75 
     76     this._filtersContainer = this._contentsElement.createChild("div", "console-filters-header hidden");
     77     this._filtersContainer.appendChild(this._filterBar.filtersElement());
     78     this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
     79     this._filterBar.setName("consoleView");
     80     this._filter.addFilters(this._filterBar);
     81 
     82     this._viewport = new WebInspector.ViewportControl(this);
     83     this._viewport.setStickToBottom(true);
     84     this._viewport.contentElement().classList.add("console-group");
     85     this._viewport.contentElement().classList.add("console-group-messages");
     86     this._contentsElement.appendChild(this._viewport.element);
     87     this._messagesElement = this._viewport.element;
     88     this._messagesElement.id = "console-messages";
     89     this._messagesElement.classList.add("monospace");
     90     this._messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
     91     this._scrolledToBottom = true;
     92 
     93     this._filterStatusMessageElement = document.createElementWithClass("div", "console-message");
     94     this._messagesElement.insertBefore(this._filterStatusMessageElement, this._messagesElement.firstChild);
     95     this._filterStatusTextElement = this._filterStatusMessageElement.createChild("span", "console-info");
     96     this._filterStatusMessageElement.createTextChild(" ");
     97     var resetFiltersLink = this._filterStatusMessageElement.createChild("span", "console-info node-link");
     98     resetFiltersLink.textContent = WebInspector.UIString("Show all messages.");
     99     resetFiltersLink.addEventListener("click", this._filter.reset.bind(this._filter), true);
    100 
    101     this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
    102     this._currentGroup = this._topGroup;
    103 
    104     this._promptElement = this._messagesElement.createChild("div", "source-code");
    105     this._promptElement.id = "console-prompt";
    106     this._promptElement.spellcheck = false;
    107     this._messagesElement.appendChild(this._promptElement);
    108     this._messagesElement.appendChild(document.createElement("br"));
    109 
    110     this._showAllMessagesCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Show all messages"));
    111     this._showAllMessagesCheckbox.inputElement.checked = true;
    112     this._showAllMessagesCheckbox.inputElement.addEventListener("change", this._updateMessageList.bind(this), false);
    113 
    114     if (!WebInspector.experimentsSettings.workersInMainWindow.isEnabled())
    115         this._showAllMessagesCheckbox.element.classList.add("hidden");
    116 
    117     statusBarElement.appendChild(this._showAllMessagesCheckbox.element);
    118 
    119     this._registerShortcuts();
    120     this.registerRequiredCSS("suggestBox.css");
    121 
    122     this._messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
    123     WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged, this);
    124 
    125     this._linkifier = new WebInspector.Linkifier();
    126 
    127     /** @type {!Array.<!WebInspector.ConsoleViewMessage>} */
    128     this._consoleMessages = [];
    129 
    130     this._prompt = new WebInspector.TextPromptWithHistory(WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext);
    131     this._prompt.setSuggestBoxEnabled(true);
    132     this._prompt.renderAsBlock();
    133     this._prompt.attach(this._promptElement);
    134     this._prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
    135     this._prompt.setHistoryData(WebInspector.settings.consoleHistory.get());
    136     var historyData = WebInspector.settings.consoleHistory.get();
    137     this._prompt.setHistoryData(historyData);
    138 
    139     this._updateFilterStatus();
    140     WebInspector.settings.consoleTimestampsEnabled.addChangeListener(this._consoleTimestampsSettingChanged, this);
    141 
    142     this._registerWithMessageSink();
    143     WebInspector.targetManager.observeTargets(this);
    144     WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executionContextChangedExternally, this);
    145 }
    146 
    147 WebInspector.ConsoleView.prototype = {
    148     /**
    149      * @return {number}
    150      */
    151     itemCount: function()
    152     {
    153         return this._visibleViewMessages.length;
    154     },
    155 
    156     /**
    157      * @param {number} index
    158      * @return {?WebInspector.ViewportElement}
    159      */
    160     itemElement: function(index)
    161     {
    162         return this._visibleViewMessages[index];
    163     },
    164 
    165     /**
    166      * @param {number} index
    167      * @return {number}
    168      */
    169     fastHeight: function(index)
    170     {
    171         return this._visibleViewMessages[index].fastHeight();
    172     },
    173 
    174     /**
    175      * @return {number}
    176      */
    177     minimumRowHeight: function()
    178     {
    179         return 16;
    180     },
    181 
    182     /**
    183      * @param {!WebInspector.Target} target
    184      */
    185     targetAdded: function(target)
    186     {
    187         /**
    188          * @param {!WebInspector.ConsoleMessage} message
    189          * @this {WebInspector.ConsoleView}
    190          */
    191         function appendMessage(message)
    192         {
    193              var viewMessage = this._createViewMessage(message);
    194              this._consoleMessageAdded(viewMessage);
    195         }
    196 
    197         target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
    198         target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
    199         target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
    200         target.consoleModel.messages.forEach(appendMessage, this);
    201         this._viewport.invalidate();
    202 
    203         target.runtimeModel.executionContexts().forEach(this._executionContextCreated, this);
    204         target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
    205         target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
    206     },
    207 
    208     /**
    209      * @param {!WebInspector.Target} target
    210      */
    211     targetRemoved: function(target)
    212     {
    213         target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
    214         target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
    215         target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
    216         target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
    217         target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
    218     },
    219 
    220     _registerWithMessageSink: function()
    221     {
    222         WebInspector.messageSink.messages().forEach(this._addSinkMessage, this);
    223         WebInspector.messageSink.addEventListener(WebInspector.MessageSink.Events.MessageAdded, messageAdded, this);
    224 
    225         /**
    226          * @param {!WebInspector.Event} event
    227          * @this {WebInspector.ConsoleView}
    228          */
    229         function messageAdded(event)
    230         {
    231             this._addSinkMessage(/** @type {!WebInspector.MessageSink.Message} */ (event.data));
    232         }
    233     },
    234 
    235     /**
    236      * @param {!WebInspector.MessageSink.Message} message
    237      */
    238     _addSinkMessage: function(message)
    239     {
    240         var level = WebInspector.ConsoleMessage.MessageLevel.Debug;
    241         switch (message.level) {
    242         case WebInspector.MessageSink.MessageLevel.Error:
    243             level = WebInspector.ConsoleMessage.MessageLevel.Error;
    244             break;
    245         case WebInspector.MessageSink.MessageLevel.Warning:
    246             level = WebInspector.ConsoleMessage.MessageLevel.Warning;
    247             break;
    248         }
    249 
    250         var consoleMessage = new WebInspector.ConsoleMessage(null, WebInspector.ConsoleMessage.MessageSource.Other, level, message.text,
    251                 undefined, undefined, undefined, undefined, undefined, undefined, undefined, message.timestamp);
    252         this._addConsoleMessage(consoleMessage);
    253     },
    254 
    255     /**
    256      * @param {!WebInspector.Event} event
    257      */
    258     _consoleTimestampsSettingChanged: function(event)
    259     {
    260         var enabled = /** @type {boolean} */ (event.data);
    261         this._updateMessageList();
    262         this._consoleMessages.forEach(function(viewMessage) {
    263             viewMessage.updateTimestamp(enabled);
    264         });
    265     },
    266 
    267     /**
    268      * @return {!Element}
    269      */
    270     defaultFocusedElement: function()
    271     {
    272         return this._promptElement
    273     },
    274 
    275     _onFiltersToggled: function(event)
    276     {
    277         var toggled = /** @type {boolean} */ (event.data);
    278         this._filtersContainer.classList.toggle("hidden", !toggled);
    279     },
    280 
    281     /**
    282      * @param {!WebInspector.ExecutionContext} executionContext
    283      * @return {string}
    284      */
    285     _titleFor: function(executionContext)
    286     {
    287         var result = executionContext.name;
    288         if (executionContext.isMainWorldContext && executionContext.frameId) {
    289             var frame = executionContext.target().resourceTreeModel.frameForId(executionContext.frameId);
    290             result =  frame ? frame.displayName() : result;
    291         }
    292 
    293         if (!executionContext.isMainWorldContext)
    294             result = "\u00a0\u00a0\u00a0\u00a0" + result;
    295 
    296         var maxLength = 50;
    297         return result.trimMiddle(maxLength);
    298     },
    299 
    300     /**
    301      * @param {!WebInspector.Event} event
    302      */
    303     _onExecutionContextCreated: function(event)
    304     {
    305         var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
    306         this._executionContextCreated(executionContext);
    307     },
    308 
    309     /**
    310      * @param {!WebInspector.ExecutionContext} executionContext
    311      */
    312     _executionContextCreated: function(executionContext)
    313     {
    314         var newOption = document.createElement("option");
    315         newOption.__executionContext = executionContext;
    316         newOption.text = this._titleFor(executionContext);
    317         this._optionByExecutionContext.put(executionContext, newOption);
    318         var sameGroupExists = false;
    319         var options = this._executionContextSelector.selectElement().options;
    320         var insertBeforeOption = null;
    321         for (var i = 0; i < options.length; ++i) {
    322             var optionContext = options[i].__executionContext;
    323             var isSameGroup = executionContext.target() === optionContext.target() && executionContext.frameId === optionContext.frameId;
    324             sameGroupExists |= isSameGroup;
    325             if ((isSameGroup && WebInspector.ExecutionContext.comparator(optionContext, executionContext) > 0) || (sameGroupExists && !isSameGroup)) {
    326                 insertBeforeOption = options[i];
    327                 break;
    328             }
    329         }
    330         this._executionContextSelector.selectElement().insertBefore(newOption, insertBeforeOption);
    331         if (executionContext === WebInspector.context.flavor(WebInspector.ExecutionContext))
    332             this._executionContextSelector.select(newOption);
    333     },
    334 
    335     /**
    336      * @param {!WebInspector.Event} event
    337      */
    338     _onExecutionContextDestroyed: function(event)
    339     {
    340         var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
    341         var option = this._optionByExecutionContext.remove(executionContext);
    342         option.remove();
    343     },
    344 
    345     _executionContextChanged: function()
    346     {
    347         var newContext = this._currentExecutionContext();
    348         WebInspector.context.setFlavor(WebInspector.ExecutionContext, newContext);
    349         this._prompt.clearAutoComplete(true);
    350         if (!this._showAllMessagesCheckbox.checked())
    351             this._updateMessageList();
    352     },
    353 
    354     /**
    355      * @param {!WebInspector.Event} event
    356      */
    357     _executionContextChangedExternally: function(event)
    358     {
    359         var executionContext =  /** @type {?WebInspector.ExecutionContext} */ (event.data);
    360         if (!executionContext)
    361             return;
    362 
    363         var options = this._executionContextSelector.selectElement().options;
    364         for (var i = 0; i < options.length; ++i) {
    365             if (options[i].__executionContext === executionContext)
    366                 this._executionContextSelector.select(options[i]);
    367         }
    368     },
    369 
    370     /**
    371      * @return {?WebInspector.ExecutionContext}
    372      */
    373     _currentExecutionContext: function()
    374     {
    375         var option = this._executionContextSelector.selectedOption();
    376         return option ? option.__executionContext : null;
    377     },
    378 
    379     willHide: function()
    380     {
    381         this._prompt.hideSuggestBox();
    382         this._prompt.clearAutoComplete(true);
    383     },
    384 
    385     wasShown: function()
    386     {
    387         this._viewport.refresh();
    388         if (!this._prompt.isCaretInsidePrompt())
    389             this._prompt.moveCaretToEndOfPrompt();
    390     },
    391 
    392     focus: function()
    393     {
    394         if (this._promptElement === WebInspector.currentFocusElement())
    395             return;
    396         WebInspector.setCurrentFocusElement(this._promptElement);
    397         this._prompt.moveCaretToEndOfPrompt();
    398     },
    399 
    400     storeScrollPositions: function()
    401     {
    402         WebInspector.View.prototype.storeScrollPositions.call(this);
    403         this._scrolledToBottom = this._messagesElement.isScrolledToBottom();
    404     },
    405 
    406     restoreScrollPositions: function()
    407     {
    408         if (this._scrolledToBottom)
    409             this._immediatelyScrollIntoView();
    410         else
    411             WebInspector.View.prototype.restoreScrollPositions.call(this);
    412     },
    413 
    414     onResize: function()
    415     {
    416         this._scheduleViewportRefresh();
    417         this._prompt.hideSuggestBox();
    418         this.restoreScrollPositions();
    419     },
    420 
    421     _isScrollIntoViewScheduled: function()
    422     {
    423         return !!this._scrollIntoViewTimer;
    424     },
    425 
    426     _scheduleViewportRefresh: function()
    427     {
    428         if (this._scrollIntoViewTimer)
    429             return;
    430         /**
    431          * @this {WebInspector.ConsoleView}
    432          */
    433         function scrollIntoView()
    434         {
    435             delete this._scrollIntoViewTimer;
    436             this._viewport.invalidate();
    437         }
    438         this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 50);
    439     },
    440 
    441     _immediatelyScrollIntoView: function()
    442     {
    443         this._promptElement.scrollIntoView(true);
    444         this._cancelScheduledScrollIntoView();
    445     },
    446 
    447     _cancelScheduledScrollIntoView: function()
    448     {
    449         if (!this._isScrollIntoViewScheduled())
    450             return;
    451         clearTimeout(this._scrollIntoViewTimer);
    452         this._viewport.refresh();
    453         delete this._scrollIntoViewTimer;
    454     },
    455 
    456     _updateFilterStatus: function()
    457     {
    458         this._filterStatusTextElement.textContent = WebInspector.UIString(this._hiddenByFilterCount === 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", this._hiddenByFilterCount);
    459         this._filterStatusMessageElement.style.display = this._hiddenByFilterCount ? "" : "none";
    460     },
    461 
    462     /**
    463      * @param {!WebInspector.ConsoleViewMessage} viewMessage
    464      */
    465     _consoleMessageAdded: function(viewMessage)
    466     {
    467         /**
    468          * @param {!WebInspector.ConsoleViewMessage} viewMessage1
    469          * @param {!WebInspector.ConsoleViewMessage} viewMessage2
    470          * @return {number}
    471          */
    472         function compareTimestamps(viewMessage1, viewMessage2)
    473         {
    474             return WebInspector.ConsoleMessage.timestampComparator(viewMessage1.consoleMessage(), viewMessage2.consoleMessage());
    475         }
    476         var insertAt = insertionIndexForObjectInListSortedByFunction(viewMessage, this._consoleMessages, compareTimestamps, true);
    477         this._consoleMessages.splice(insertAt, 0, viewMessage);
    478 
    479         var message = viewMessage.consoleMessage();
    480         if (this._urlToMessageCount[message.url])
    481             this._urlToMessageCount[message.url]++;
    482         else
    483             this._urlToMessageCount[message.url] = 1;
    484 
    485         if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
    486             return;
    487 
    488         if (this._filter.shouldBeVisible(viewMessage))
    489             this._showConsoleMessage(viewMessage)
    490         else {
    491             this._hiddenByFilterCount++;
    492             this._updateFilterStatus();
    493         }
    494     },
    495 
    496     /**
    497      * @param {!WebInspector.Event} event
    498      */
    499     _onConsoleMessageAdded: function(event)
    500     {
    501         var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data);
    502         this._addConsoleMessage(message);
    503     },
    504 
    505     /**
    506      * @param {!WebInspector.ConsoleMessage} message
    507      */
    508     _addConsoleMessage: function(message)
    509     {
    510         var viewMessage = this._createViewMessage(message);
    511         this._consoleMessageAdded(viewMessage);
    512         this._scheduleViewportRefresh();
    513     },
    514 
    515     /**
    516      * @param {!WebInspector.ConsoleViewMessage} viewMessage
    517      */
    518     _showConsoleMessage: function(viewMessage)
    519     {
    520         var lastMessage = this._visibleViewMessages.peekLast();
    521         if (viewMessage.consoleMessage().type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
    522             if (lastMessage && !this._currentGroup.messagesHidden())
    523                 lastMessage.incrementCloseGroupDecorationCount();
    524             this._currentGroup = this._currentGroup.parentGroup();
    525             return;
    526         }
    527         if (!this._currentGroup.messagesHidden()) {
    528             var originatingMessage = viewMessage.consoleMessage().originatingMessage();
    529             if (lastMessage && originatingMessage && lastMessage.consoleMessage() === originatingMessage)
    530                 lastMessage.toMessageElement().classList.add("console-adjacent-user-command-result");
    531 
    532             this._visibleViewMessages.push(viewMessage);
    533 
    534             if (this._searchRegex && viewMessage.matchesRegex(this._searchRegex)) {
    535                 this._searchResults.push(viewMessage);
    536                 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
    537             }
    538         }
    539 
    540         if (viewMessage.consoleMessage().isGroupStartMessage())
    541             this._currentGroup = new WebInspector.ConsoleGroup(this._currentGroup, viewMessage);
    542     },
    543 
    544     /**
    545      * @param {!WebInspector.ConsoleMessage} message
    546      * @return {!WebInspector.ConsoleViewMessage}
    547      */
    548     _createViewMessage: function(message)
    549     {
    550         var nestingLevel = this._currentGroup.nestingLevel();
    551         switch (message.type) {
    552         case WebInspector.ConsoleMessage.MessageType.Command:
    553             return new WebInspector.ConsoleCommand(message, nestingLevel);
    554         case WebInspector.ConsoleMessage.MessageType.Result:
    555             return new WebInspector.ConsoleCommandResult(message, this._linkifier, nestingLevel);
    556         case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
    557         case WebInspector.ConsoleMessage.MessageType.StartGroup:
    558             return new WebInspector.ConsoleGroupViewMessage(message, this._linkifier, nestingLevel);
    559         default:
    560             return new WebInspector.ConsoleViewMessage(message, this._linkifier, nestingLevel);
    561         }
    562     },
    563 
    564     _consoleCleared: function()
    565     {
    566         this._clearCurrentSearchResultHighlight();
    567         this._consoleMessages = [];
    568         this._scrolledToBottom = true;
    569         this._updateMessageList();
    570 
    571         if (this._searchRegex)
    572             this._searchableView.updateSearchMatchesCount(0);
    573 
    574         this._linkifier.reset();
    575     },
    576 
    577     _handleContextMenuEvent: function(event)
    578     {
    579         if (event.target.enclosingNodeOrSelfWithNodeName("a"))
    580             return;
    581 
    582         var contextMenu = new WebInspector.ContextMenu(event);
    583 
    584         function monitoringXHRItemAction()
    585         {
    586             WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
    587         }
    588         contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction, WebInspector.settings.monitoringXHREnabled.get());
    589 
    590         function preserveLogItemAction()
    591         {
    592             WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get());
    593         }
    594         contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction, WebInspector.settings.preserveConsoleLog.get());
    595 
    596         var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message-wrapper");
    597         var consoleMessage = sourceElement ? sourceElement.message.consoleMessage() : null;
    598 
    599         var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
    600 
    601         if (consoleMessage && consoleMessage.url) {
    602             var menuTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(consoleMessage.url).displayName);
    603             filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
    604         }
    605 
    606         filterSubMenu.appendSeparator();
    607         var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._filter.removeMessageURLFilter.bind(this._filter));
    608         filterSubMenu.appendSeparator();
    609 
    610         var hasFilters = false;
    611 
    612         for (var url in this._filter.messageURLFilters) {
    613             filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._filter.removeMessageURLFilter.bind(this._filter, url), true);
    614             hasFilters = true;
    615         }
    616 
    617         filterSubMenu.setEnabled(hasFilters || (consoleMessage && consoleMessage.url));
    618         unhideAll.setEnabled(hasFilters);
    619 
    620         contextMenu.appendSeparator();
    621         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
    622 
    623         var request = consoleMessage ? consoleMessage.request : null;
    624         if (request && request.type === WebInspector.resourceTypes.XHR) {
    625             contextMenu.appendSeparator();
    626             contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
    627         }
    628 
    629         contextMenu.show();
    630     },
    631 
    632     /**
    633      * @param {!WebInspector.ConsoleViewMessage} lastMessage
    634      * @param {?WebInspector.ConsoleViewMessage} viewMessage
    635      * @return {boolean}
    636      */
    637     _tryToCollapseMessages: function(lastMessage, viewMessage)
    638     {
    639         if (!WebInspector.settings.consoleTimestampsEnabled.get() && viewMessage && !lastMessage.consoleMessage().isGroupMessage() && lastMessage.consoleMessage().isEqual(viewMessage.consoleMessage())) {
    640             viewMessage.incrementRepeatCount();
    641             return true;
    642         }
    643 
    644         return false;
    645     },
    646 
    647     _updateMessageList: function()
    648     {
    649         this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
    650         this._currentGroup = this._topGroup;
    651         this._searchResults = [];
    652         this._hiddenByFilterCount = 0;
    653         for (var i = 0; i < this._visibleViewMessages.length; ++i) {
    654             this._visibleViewMessages[i].resetCloseGroupDecorationCount();
    655             this._visibleViewMessages[i].resetIncrementRepeatCount();
    656         }
    657         this._visibleViewMessages = [];
    658         for (var i = 0; i < this._consoleMessages.length; ++i) {
    659             var viewMessage = this._consoleMessages[i];
    660             if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
    661                 continue;
    662             if (this._filter.shouldBeVisible(viewMessage))
    663                 this._showConsoleMessage(viewMessage);
    664             else
    665                 this._hiddenByFilterCount++;
    666         }
    667         this._updateFilterStatus();
    668         this._viewport.invalidate();
    669     },
    670 
    671     /**
    672      * @param {!WebInspector.Event} event
    673      */
    674     _monitoringXHREnabledSettingChanged: function(event)
    675     {
    676         var enabled = /** @type {boolean} */ (event.data);
    677         WebInspector.targetManager.targets().forEach(function(target) {target.consoleAgent().setMonitoringXHREnabled(enabled);});
    678     },
    679 
    680     /**
    681      * @param {?Event} event
    682      */
    683     _messagesClicked: function(event)
    684     {
    685         if (!this._prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
    686             this._prompt.moveCaretToEndOfPrompt();
    687         var groupMessage = event.target.enclosingNodeOrSelfWithClass("console-group-title");
    688         if (!groupMessage)
    689             return;
    690         var consoleGroupViewMessage = groupMessage.parentElement.message;
    691         consoleGroupViewMessage.setCollapsed(!consoleGroupViewMessage.collapsed());
    692         this._updateMessageList();
    693     },
    694 
    695     _registerShortcuts: function()
    696     {
    697         this._shortcuts = {};
    698 
    699         var shortcut = WebInspector.KeyboardShortcut;
    700         var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
    701 
    702         var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
    703         this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
    704         var keys = [shortcutL];
    705         if (WebInspector.isMac()) {
    706             var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
    707             this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
    708             keys.unshift(shortcutK);
    709         }
    710         section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
    711 
    712         section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
    713         section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
    714 
    715         var shortcutU = shortcut.makeDescriptor("u", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
    716         this._shortcuts[shortcutU.key] = this._clearPromptBackwards.bind(this);
    717         section.addAlternateKeys([shortcutU], WebInspector.UIString("Clear console prompt"));
    718 
    719         keys = [
    720             shortcut.makeDescriptor(shortcut.Keys.Down),
    721             shortcut.makeDescriptor(shortcut.Keys.Up)
    722         ];
    723         section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
    724 
    725         if (WebInspector.isMac()) {
    726             keys = [
    727                 shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
    728                 shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
    729             ];
    730             section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
    731         }
    732 
    733         section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
    734     },
    735 
    736     _clearPromptBackwards: function()
    737     {
    738         this._prompt.text = "";
    739     },
    740 
    741     _requestClearMessages: function()
    742     {
    743         WebInspector.console.requestClearMessages();
    744     },
    745 
    746     _promptKeyDown: function(event)
    747     {
    748         if (isEnterKey(event)) {
    749             this._enterKeyPressed(event);
    750             return;
    751         }
    752 
    753         var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
    754         var handler = this._shortcuts[shortcut];
    755         if (handler) {
    756             handler();
    757             event.preventDefault();
    758         }
    759     },
    760 
    761     _enterKeyPressed: function(event)
    762     {
    763         if (event.altKey || event.ctrlKey || event.shiftKey)
    764             return;
    765 
    766         event.consume(true);
    767 
    768         this._prompt.clearAutoComplete(true);
    769 
    770         var str = this._prompt.text;
    771         if (!str.length)
    772             return;
    773         this._appendCommand(str, true);
    774     },
    775 
    776     /**
    777      * @param {?WebInspector.RemoteObject} result
    778      * @param {boolean} wasThrown
    779      * @param {!WebInspector.ConsoleMessage} originatingConsoleMessage
    780      */
    781     _printResult: function(result, wasThrown, originatingConsoleMessage)
    782     {
    783         if (!result)
    784             return;
    785 
    786         var target = result.target();
    787         /**
    788          * @param {string=} url
    789          * @param {number=} lineNumber
    790          * @param {number=} columnNumber
    791          */
    792         function addMessage(url, lineNumber, columnNumber)
    793         {
    794             var level = wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
    795             var message = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, url, lineNumber, columnNumber, undefined, [result]);
    796             message.setOriginatingMessage(originatingConsoleMessage);
    797             target.consoleModel.addMessage(message);
    798         }
    799 
    800         if (result.type !== "function") {
    801             addMessage();
    802             return;
    803         }
    804 
    805         result.functionDetails(didGetDetails);
    806 
    807         /**
    808          * @param {?DebuggerAgent.FunctionDetails} response
    809          */
    810         function didGetDetails(response)
    811         {
    812             if (!response) {
    813                 addMessage();
    814                 return;
    815             }
    816 
    817             var url;
    818             var lineNumber;
    819             var columnNumber;
    820             var script = target.debuggerModel.scriptForId(response.location.scriptId);
    821             if (script && script.sourceURL) {
    822                 url = script.sourceURL;
    823                 lineNumber = response.location.lineNumber + 1;
    824                 columnNumber = response.location.columnNumber + 1;
    825             }
    826             // FIXME: this should be using live location.
    827             addMessage(url, lineNumber, columnNumber);
    828         }
    829     },
    830 
    831     /**
    832      * @param {string} text
    833      * @param {boolean} useCommandLineAPI
    834      */
    835     _appendCommand: function(text, useCommandLineAPI)
    836     {
    837 
    838         this._prompt.text = "";
    839         var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
    840         if (currentExecutionContext)
    841             WebInspector.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, text, useCommandLineAPI);
    842     },
    843 
    844     /**
    845      * @param {!WebInspector.Event} event
    846      */
    847     _commandEvaluated: function(event)
    848     {
    849         var data = /**{{result: ?WebInspector.RemoteObject, wasThrown: boolean, text: string, commandMessage: !WebInspector.ConsoleMessage}} */ (event.data);
    850         this._prompt.pushHistoryItem(data.text);
    851         WebInspector.settings.consoleHistory.set(this._prompt.historyData.slice(-30));
    852         this._printResult(data.result, data.wasThrown, data.commandMessage);
    853     },
    854 
    855     /**
    856      * @return {!Array.<!Element>}
    857      */
    858     elementsToRestoreScrollPositionsFor: function()
    859     {
    860         return [this._messagesElement];
    861     },
    862 
    863     searchCanceled: function()
    864     {
    865         this._clearCurrentSearchResultHighlight();
    866         delete this._searchResults;
    867         delete this._searchRegex;
    868         this._viewport.refresh();
    869     },
    870 
    871     /**
    872      * @param {string} query
    873      * @param {boolean} shouldJump
    874      * @param {boolean=} jumpBackwards
    875      */
    876     performSearch: function(query, shouldJump, jumpBackwards)
    877     {
    878         this.searchCanceled();
    879         this._searchableView.updateSearchMatchesCount(0);
    880         this._searchRegex = createPlainTextSearchRegex(query, "gi");
    881 
    882         /** @type {!Array.<number>} */
    883         this._searchResults = [];
    884         for (var i = 0; i < this._visibleViewMessages.length; i++) {
    885             if (this._visibleViewMessages[i].matchesRegex(this._searchRegex))
    886                 this._searchResults.push(i);
    887         }
    888         this._searchableView.updateSearchMatchesCount(this._searchResults.length);
    889         this._currentSearchResultIndex = -1;
    890         if (shouldJump && this._searchResults.length)
    891             this._jumpToSearchResult(jumpBackwards ? -1 : 0);
    892         this._viewport.refresh();
    893     },
    894 
    895     jumpToNextSearchResult: function()
    896     {
    897         if (!this._searchResults || !this._searchResults.length)
    898             return;
    899         this._jumpToSearchResult(this._currentSearchResultIndex + 1);
    900     },
    901 
    902     jumpToPreviousSearchResult: function()
    903     {
    904         if (!this._searchResults || !this._searchResults.length)
    905             return;
    906         this._jumpToSearchResult(this._currentSearchResultIndex - 1);
    907     },
    908 
    909     _clearCurrentSearchResultHighlight: function()
    910     {
    911         if (!this._searchResults)
    912             return;
    913 
    914         var highlightedViewMessage = this._visibleViewMessages[this._searchResults[this._currentSearchResultIndex]];
    915         if (highlightedViewMessage)
    916             highlightedViewMessage.clearHighlight();
    917         this._currentSearchResultIndex = -1;
    918     },
    919 
    920     _jumpToSearchResult: function(index)
    921     {
    922         index = mod(index, this._searchResults.length);
    923         this._clearCurrentSearchResultHighlight();
    924         this._currentSearchResultIndex = index;
    925         this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex);
    926         var currentViewMessageIndex = this._searchResults[index];
    927         this._viewport.scrollItemIntoView(currentViewMessageIndex);
    928         this._visibleViewMessages[currentViewMessageIndex].highlightSearchResults(this._searchRegex);
    929     },
    930 
    931     __proto__: WebInspector.VBox.prototype
    932 }
    933 
    934 /**
    935  * @constructor
    936  * @extends {WebInspector.Object}
    937  * @param {!WebInspector.ConsoleView} view
    938  */
    939 WebInspector.ConsoleViewFilter = function(view)
    940 {
    941     this._view = view;
    942     this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
    943     this._filterChanged = this.dispatchEventToListeners.bind(this, WebInspector.ConsoleViewFilter.Events.FilterChanged);
    944 };
    945 
    946 WebInspector.ConsoleViewFilter.Events = {
    947     FilterChanged: "FilterChanged"
    948 };
    949 
    950 WebInspector.ConsoleViewFilter.prototype = {
    951     addFilters: function(filterBar)
    952     {
    953         this._textFilterUI = new WebInspector.TextFilterUI(true);
    954         this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
    955         filterBar.addFilter(this._textFilterUI);
    956 
    957         var levels = [
    958             {name: "error", label: WebInspector.UIString("Errors")},
    959             {name: "warning", label: WebInspector.UIString("Warnings")},
    960             {name: "info", label: WebInspector.UIString("Info")},
    961             {name: "log", label: WebInspector.UIString("Logs")},
    962             {name: "debug", label: WebInspector.UIString("Debug")}
    963         ];
    964         this._levelFilterUI = new WebInspector.NamedBitSetFilterUI(levels, WebInspector.settings.messageLevelFilters);
    965         this._levelFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
    966         filterBar.addFilter(this._levelFilterUI);
    967     },
    968 
    969     _textFilterChanged: function(event)
    970     {
    971         this._filterRegex = this._textFilterUI.regex();
    972 
    973         this._filterChanged();
    974     },
    975 
    976     /**
    977      * @param {string} url
    978      */
    979     addMessageURLFilter: function(url)
    980     {
    981         this._messageURLFilters[url] = true;
    982         WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
    983         this._filterChanged();
    984     },
    985 
    986     /**
    987      * @param {string} url
    988      */
    989     removeMessageURLFilter: function(url)
    990     {
    991         if (!url)
    992             this._messageURLFilters = {};
    993         else
    994             delete this._messageURLFilters[url];
    995 
    996         WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
    997         this._filterChanged();
    998     },
    999 
   1000     /**
   1001      * @returns {!Object}
   1002      */
   1003     get messageURLFilters()
   1004     {
   1005         return this._messageURLFilters;
   1006     },
   1007 
   1008     /**
   1009      * @param {!WebInspector.ConsoleViewMessage} viewMessage
   1010      * @return {boolean}
   1011      */
   1012     shouldBeVisible: function(viewMessage)
   1013     {
   1014         var message = viewMessage.consoleMessage();
   1015         var executionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
   1016         if (!message.target())
   1017             return true;
   1018 
   1019         if (!this._view._showAllMessagesCheckbox.checked() && executionContext && (message.target() !== executionContext.target() || message.executionContextId !== executionContext.id))
   1020             return false;
   1021 
   1022         if (viewMessage.consoleMessage().isGroupMessage())
   1023             return true;
   1024 
   1025         if (message.type === WebInspector.ConsoleMessage.MessageType.Result || message.type === WebInspector.ConsoleMessage.MessageType.Command)
   1026             return true;
   1027 
   1028         if (message.url && this._messageURLFilters[message.url])
   1029             return false;
   1030 
   1031         if (message.level && !this._levelFilterUI.accept(message.level))
   1032             return false;
   1033 
   1034         if (this._filterRegex) {
   1035             this._filterRegex.lastIndex = 0;
   1036             if (!viewMessage.matchesRegex(this._filterRegex))
   1037                 return false;
   1038         }
   1039 
   1040         return true;
   1041     },
   1042 
   1043     reset: function()
   1044     {
   1045         this._messageURLFilters = {};
   1046         WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
   1047         WebInspector.settings.messageLevelFilters.set({});
   1048         this._view._showAllMessagesCheckbox.inputElement.checked = true;
   1049         this._textFilterUI.setValue("");
   1050         this._filterChanged();
   1051     },
   1052 
   1053     __proto__: WebInspector.Object.prototype
   1054 };
   1055 
   1056 
   1057 /**
   1058  * @constructor
   1059  * @extends {WebInspector.ConsoleViewMessage}
   1060  * @param {!WebInspector.ConsoleMessage} message
   1061  * @param {number} nestingLevel
   1062  */
   1063 WebInspector.ConsoleCommand = function(message, nestingLevel)
   1064 {
   1065     WebInspector.ConsoleViewMessage.call(this, message, null, nestingLevel);
   1066 }
   1067 
   1068 WebInspector.ConsoleCommand.prototype = {
   1069     clearHighlight: function()
   1070     {
   1071         var highlightedMessage = this._formattedCommand;
   1072         delete this._formattedCommand;
   1073         this._formatCommand();
   1074         this._element.replaceChild(this._formattedCommand, highlightedMessage);
   1075     },
   1076 
   1077     /**
   1078      * @param {!RegExp} regexObject
   1079      */
   1080     highlightSearchResults: function(regexObject)
   1081     {
   1082         regexObject.lastIndex = 0;
   1083         var match = regexObject.exec(this.text);
   1084         var matchRanges = [];
   1085         while (match) {
   1086             matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
   1087             match = regexObject.exec(this.text);
   1088         }
   1089         WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
   1090         this._element.scrollIntoViewIfNeeded();
   1091     },
   1092 
   1093     /**
   1094      * @param {!RegExp} regexObject
   1095      * @return {boolean}
   1096      */
   1097     matchesRegex: function(regexObject)
   1098     {
   1099         regexObject.lastIndex = 0;
   1100         return regexObject.test(this.text);
   1101     },
   1102 
   1103     /**
   1104      * @return {!Element}
   1105      */
   1106     contentElement: function()
   1107     {
   1108         if (!this._element) {
   1109             this._element = document.createElement("div");
   1110             this._element.message = this;
   1111             this._element.className = "console-user-command";
   1112 
   1113             this._formatCommand();
   1114             this._element.appendChild(this._formattedCommand);
   1115         }
   1116         return this._element;
   1117     },
   1118 
   1119     _formatCommand: function()
   1120     {
   1121         this._formattedCommand = document.createElement("span");
   1122         this._formattedCommand.className = "console-message-text source-code";
   1123         this._formattedCommand.textContent = this.text;
   1124     },
   1125 
   1126     __proto__: WebInspector.ConsoleViewMessage.prototype
   1127 }
   1128 
   1129 /**
   1130  * @constructor
   1131  * @extends {WebInspector.ConsoleViewMessage}
   1132  * @param {!WebInspector.ConsoleMessage} message
   1133  * @param {!WebInspector.Linkifier} linkifier
   1134  * @param {number} nestingLevel
   1135  */
   1136 WebInspector.ConsoleCommandResult = function(message, linkifier, nestingLevel)
   1137 {
   1138     WebInspector.ConsoleViewMessage.call(this, message, linkifier, nestingLevel);
   1139 }
   1140 
   1141 WebInspector.ConsoleCommandResult.prototype = {
   1142     /**
   1143      * @override
   1144      * @param {!WebInspector.RemoteObject} array
   1145      * @return {boolean}
   1146      */
   1147     useArrayPreviewInFormatter: function(array)
   1148     {
   1149         return false;
   1150     },
   1151 
   1152     /**
   1153      * @return {!Element}
   1154      */
   1155     contentElement: function()
   1156     {
   1157         var element = WebInspector.ConsoleViewMessage.prototype.contentElement.call(this);
   1158         element.classList.add("console-user-command-result");
   1159         return element;
   1160     },
   1161 
   1162     __proto__: WebInspector.ConsoleViewMessage.prototype
   1163 }
   1164 
   1165 /**
   1166  * @constructor
   1167  * @param {?WebInspector.ConsoleGroup} parentGroup
   1168  * @param {?WebInspector.ConsoleViewMessage} groupMessage
   1169  */
   1170 WebInspector.ConsoleGroup = function(parentGroup, groupMessage)
   1171 {
   1172     this._parentGroup = parentGroup;
   1173     this._nestingLevel = parentGroup ? parentGroup.nestingLevel() + 1 : 0;
   1174     this._messagesHidden = groupMessage && groupMessage.collapsed() || this._parentGroup && this._parentGroup.messagesHidden();
   1175 }
   1176 
   1177 /**
   1178  * @return {!WebInspector.ConsoleGroup}
   1179  */
   1180 WebInspector.ConsoleGroup.createTopGroup = function()
   1181 {
   1182     return new WebInspector.ConsoleGroup(null, null);
   1183 }
   1184 
   1185 WebInspector.ConsoleGroup.prototype = {
   1186     /**
   1187      * @return {boolean}
   1188      */
   1189     messagesHidden: function()
   1190     {
   1191         return this._messagesHidden;
   1192     },
   1193 
   1194     /**
   1195      * @return {number}
   1196      */
   1197     nestingLevel: function()
   1198     {
   1199         return this._nestingLevel;
   1200     },
   1201 
   1202     /**
   1203      * @return {?WebInspector.ConsoleGroup}
   1204      */
   1205     parentGroup: function()
   1206     {
   1207         return this._parentGroup || this;
   1208     },
   1209 }
   1210 
   1211 /**
   1212  * @constructor
   1213  * @implements {WebInspector.ActionDelegate}
   1214  */
   1215 WebInspector.ConsoleView.ShowConsoleActionDelegate = function()
   1216 {
   1217 }
   1218 
   1219 WebInspector.ConsoleView.ShowConsoleActionDelegate.prototype = {
   1220     /**
   1221      * @return {boolean}
   1222      */
   1223     handleAction: function()
   1224     {
   1225         WebInspector.console.show();
   1226         return true;
   1227     }
   1228 }
   1229