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