chrome-devtools-frontend
Version:
Chrome DevTools UI
1,227 lines (1,097 loc) ⢠69.3 kB
text/typescript
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2009 Joseph Pecoraro
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* eslint-disable rulesdir/no_underscored_properties */
import * as Bindings from '../bindings/bindings.js';
import * as BrowserSDK from '../browser_sdk/browser_sdk.js';
import * as Common from '../common/common.js';
import * as Components from '../components/components.js';
import * as Host from '../host/host.js';
import * as i18n from '../i18n/i18n.js';
import * as Platform from '../platform/platform.js';
import * as SDK from '../sdk/sdk.js';
import * as TextUtils from '../text_utils/text_utils.js';
import * as UIComponents from '../ui/components/components.js';
import * as UI from '../ui/ui.js';
import {ConsoleContextSelector} from './ConsoleContextSelector.js';
import {ConsoleFilter, FilterType, LevelsMask} from './ConsoleFilter.js';
import {ConsolePinPane} from './ConsolePinPane.js';
import {ConsolePrompt, Events as ConsolePromptEvents} from './ConsolePrompt.js';
import {ConsoleSidebar, Events} from './ConsoleSidebar.js';
import {ConsoleCommand, ConsoleCommandResult, ConsoleGroupViewMessage, ConsoleTableMessageView, ConsoleViewMessage, getMessageForElement, MaxLengthForLinks} from './ConsoleViewMessage.js'; // eslint-disable-line no-unused-vars
import {ConsoleViewport, ConsoleViewportElement, ConsoleViewportProvider} from './ConsoleViewport.js'; // eslint-disable-line no-unused-vars
export const UIStrings = {
/**
*@description Label for link to issues tab
*/
viewIssues: 'View issues',
/**
*@description Label for link to issues tab
*/
noIssue: 'No Issues',
/**
*@description Label for link to issues tab
*/
oneIssue: '1 Issue',
/**
*@description Label for link to issues tab
*@example {13} issueCount
*/
multipleIssues: '{issueCount} Issues',
/**
*@description Text for the tooltip of the issue counter toolbar item
*/
issueToolbarTooltipGeneral: 'Some problems no longer generate console messages, but are surfaced in the issues tab.',
/**
*@description Text for the tooltip of the issue counter toolbar item
*/
issueToolbarTooltipHaveNoIssues: 'Click to go to the issues tab.',
/**
*@description Text for the tooltip of the issue counter toolbar item
*/
issueToolbarTooltipHaveOneIssues: 'Click to view 1 issue',
/**
*@description Text for the tooltip of the issue counter toolbar item
*@example {12} issueCount
*/
issueToolbarTooltipHaveMultipleIssues: 'Click to view {issueCount} issues',
/**
*@description Infobar text about messages being on the issues tab
*/
someMessagesHaveBeenMovedToThe: 'Some messages have been moved to the Issues panel.',
/**
*@description Text in Console View of the Console panel
*/
findStringInLogs: 'Find string in logs',
/**
*@description Tooltip text that appears when hovering over the largeicon settings gear in show settings pane setting in console view of the console panel
*/
consoleSettings: 'Console settings',
/**
*@description Title of a setting under the Console category that can be invoked through the Command Menu
*/
groupSimilarMessagesInConsole: 'Group similar messages in console',
/**
*@description Title of the sidebar in the Console
*/
consoleSidebar: 'console sidebar',
/**
*@description Tooltip text that appears on the setting to preserve log when hovering over the item
*/
doNotClearLogOnPageReload: 'Do not clear log on page reload / navigation',
/**
*@description Text to preserve the log after refreshing
*/
preserveLog: 'Preserve log',
/**
*@description Text in Console View of the Console panel
*/
hideNetwork: 'Hide network',
/**
*@description Tooltip text that appears on the setting when hovering over it in Console View of the Console panel
*/
onlyShowMessagesFromTheCurrentContext:
'Only show messages from the current context (`top`, `iframe`, `worker`, extension)',
/**
*@description Alternative title text of a setting in Console View of the Console panel
*/
selectedContextOnly: 'Selected context only',
/**
*@description Tooltip text that appears on the setting when hovering over it in Console View of the Console panel
*/
eagerlyEvaluateTextInThePrompt: 'Eagerly evaluate text in the prompt',
/**
*@description Text in Console View of the Console panel
*@example {3} PH1
*/
sHidden: '{PH1} hidden',
/**
*@description Alert message for screen readers when the console is cleared
*/
consoleCleared: 'Console cleared',
/**
*@description Text in Console View of the Console panel
*@example {index.js} PH1
*/
hideMessagesFromS: 'Hide messages from {PH1}',
/**
*@description Text to save content as a specific file type
*/
saveAs: 'Save as...',
/**
*@description A context menu item in the Console View of the Console panel
*/
copyVisibleStyledSelection: 'Copy visible styled selection',
/**
*@description Text to replay an XHR request
*/
replayXhr: 'Replay XHR',
/**
*@description Text to indicate DevTools is writing to a file
*/
writingFile: 'Writing fileā¦',
/**
*@description Text to indicate the searching is in progress
*/
searching: 'Searchingā¦',
/**
*@description Text to filter result items
*/
filter: 'Filter',
/**
*@description Text in Console View of the Console panel
*/
egEventdCdnUrlacom: 'e.g. `/event\d/ -cdn url:a.com`',
/**
*@description Sdk console message message level verbose of level Labels in Console View of the Console panel
*/
verbose: 'Verbose',
/**
*@description Sdk console message message level info of level Labels in Console View of the Console panel
*/
info: 'Info',
/**
*@description Sdk console message message level warning of level Labels in Console View of the Console panel
*/
warnings: 'Warnings',
/**
*@description Text for errors
*/
errors: 'Errors',
/**
*@description Text in Console View of the Console panel
*/
logLevels: 'Log levels',
/**
*@description Title text of a setting in Console View of the Console panel
*/
overriddenByFilterSidebar: 'Overridden by filter sidebar',
/**
*@description Text in Console View of the Console panel
*/
customLevels: 'Custom levels',
/**
*@description Text in Console View of the Console panel
*@example {Warnings} PH1
*/
sOnly: '{PH1} only',
/**
*@description Text in Console View of the Console panel
*/
allLevels: 'All levels',
/**
*@description Text in Console View of the Console panel
*/
defaultLevels: 'Default levels',
/**
*@description Text in Console View of the Console panel
*/
hideAll: 'Hide all',
/**
*@description Title of level menu button in console view of the console panel
*@example {All levels} PH1
*/
logLevelS: 'Log level: {PH1}',
/**
*@description A context menu item in the Console View of the Console panel
*/
default: 'Default',
};
const str_ = i18n.i18n.registerUIStrings('console/ConsoleView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
let consoleViewInstance: ConsoleView;
/**
* This is a proper `ConsoleViewportElement` for the issue banner which can be inserted into the
* `ConsoleViewport`. To make it play nicely, it is fakes being {ConsoleViewMessage} by implementing
* `fastHeight` and `toExportString`.
*/
class IssueMessage implements ConsoleViewportElement {
_cachedIssueBarHeight: number;
_issueBar: UI.Infobar.Infobar|null;
constructor() {
this._cachedIssueBarHeight = 0;
this._issueBar = null;
}
willHide(): void {
this._cachedIssueBarHeight = this._issueBar && this._issueBar.element.offsetHeight || 0;
}
wasShown(): void {
}
element(): HTMLElement {
if (!this._issueBar) {
const issueBarAction = ({
text: i18nString(UIStrings.viewIssues),
highlight: false,
delegate: (): void => {
Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.ConsoleInfoBar);
UI.ViewManager.ViewManager.instance().showView('issues-pane');
},
dismiss: false,
} as UI.Infobar.InfobarAction);
this._issueBar = new UI.Infobar.Infobar(
UI.Infobar.Type.Issue, i18nString(UIStrings.someMessagesHaveBeenMovedToThe), [issueBarAction]);
this._issueBar.element.tabIndex = -1;
this._issueBar.element.classList.add('console-message-wrapper');
}
return this._issueBar.element;
}
focusLastChildOrSelf(): void {
if (!this._issueBar) {
return;
}
this._issueBar.element.focus();
}
fastHeight(): number {
return this._cachedIssueBarHeight || 37;
}
toExportString(): Common.UIString.LocalizedString {
return i18nString(UIStrings.someMessagesHaveBeenMovedToThe);
}
}
export class ConsoleView extends UI.Widget.VBox implements UI.SearchableView.Searchable, ConsoleViewportProvider {
_searchableView: UI.SearchableView.SearchableView;
_sidebar: ConsoleSidebar;
_isSidebarOpen: boolean;
_filter: ConsoleViewFilter;
_consoleToolbarContainer: HTMLElement;
_splitWidget: UI.SplitWidget.SplitWidget;
_contentsElement: UI.Widget.WidgetElement;
_visibleViewMessages: ConsoleViewMessage[];
_hiddenByFilterCount: number;
_shouldBeHiddenCache: Set<ConsoleViewMessage>;
_lastShownHiddenByFilterCount!: number;
_currentMatchRangeIndex!: number;
_searchRegex!: RegExp|null;
_groupableMessages: Map<string, ConsoleViewMessage[]>;
_groupableMessageTitle: Map<string, ConsoleViewMessage>;
_shortcuts: Map<number, () => void>;
_regexMatchRanges: RegexMatchRange[];
_consoleContextSelector: ConsoleContextSelector;
_filterStatusText: UI.Toolbar.ToolbarText;
_showSettingsPaneSetting: Common.Settings.Setting<boolean>;
_showSettingsPaneButton: UI.Toolbar.ToolbarSettingToggle;
_progressToolbarItem: UI.Toolbar.ToolbarItem;
_groupSimilarSetting: Common.Settings.Setting<boolean>;
_preserveLogCheckbox: UI.Toolbar.ToolbarSettingCheckbox;
_hideNetworkMessagesCheckbox: UI.Toolbar.ToolbarSettingCheckbox;
_timestampsSetting: Common.Settings.Setting<unknown>;
_consoleHistoryAutocompleteSetting: Common.Settings.Setting<boolean>;
_pinPane: ConsolePinPane;
_viewport: ConsoleViewport;
_messagesElement: HTMLElement;
_viewportThrottler: Common.Throttler.Throttler;
_pendingBatchResize: boolean;
_onMessageResizedBound: (e: Common.EventTarget.EventTargetEvent) => void;
_topGroup: ConsoleGroup;
_currentGroup: ConsoleGroup;
_promptElement: HTMLElement;
_linkifier: Components.Linkifier.Linkifier;
_consoleMessages: ConsoleViewMessage[];
_viewMessageSymbol: symbol;
_consoleHistorySetting: Common.Settings.Setting<string[]>;
_prompt: ConsolePrompt;
_issueMessage: IssueMessage|undefined;
_immediatelyFilterMessagesForTest?: boolean;
_maybeDirtyWhileMuted?: boolean;
_scheduledRefreshPromiseForTest?: Promise<void>;
_needsFullUpdate?: boolean;
_buildHiddenCacheTimeout?: number;
_searchShouldJumpBackwards?: boolean;
_searchProgressIndicator?: UI.ProgressIndicator.ProgressIndicator;
_innerSearchTimeoutId?: number;
_muteViewportUpdates?: boolean;
_waitForScrollTimeout?: number;
_issuesCounter: UIComponents.IconButton.IconButton;
constructor() {
super();
this.setMinimumSize(0, 35);
this.registerRequiredCSS('console/consoleView.css', {enableLegacyPatching: true});
this.registerRequiredCSS('object_ui/objectValue.css', {enableLegacyPatching: true});
this._searchableView = new UI.SearchableView.SearchableView(this, null);
this._searchableView.element.classList.add('console-searchable-view');
this._searchableView.setPlaceholder(i18nString(UIStrings.findStringInLogs));
this._searchableView.setMinimalSearchQuerySize(0);
this._sidebar = new ConsoleSidebar();
this._sidebar.addEventListener(Events.FilterSelected, this._onFilterChanged.bind(this));
this._isSidebarOpen = false;
this._filter = new ConsoleViewFilter(this._onFilterChanged.bind(this));
this._consoleToolbarContainer = this.element.createChild('div', 'console-toolbar-container');
this._splitWidget = new UI.SplitWidget.SplitWidget(
true /* isVertical */, false /* secondIsSidebar */, 'console.sidebar.width', 100);
this._splitWidget.setMainWidget(this._searchableView);
this._splitWidget.setSidebarWidget(this._sidebar);
this._splitWidget.show(this.element);
this._splitWidget.hideSidebar();
this._splitWidget.enableShowModeSaving();
this._isSidebarOpen = this._splitWidget.showMode() === UI.SplitWidget.ShowMode.Both;
this._filter.setLevelMenuOverridden(this._isSidebarOpen);
this._splitWidget.addEventListener(UI.SplitWidget.Events.ShowModeChanged, event => {
this._isSidebarOpen = event.data === UI.SplitWidget.ShowMode.Both;
this._filter.setLevelMenuOverridden(this._isSidebarOpen);
this._onFilterChanged();
});
this._contentsElement = this._searchableView.element;
this.element.classList.add('console-view');
this._visibleViewMessages = [];
this._hiddenByFilterCount = 0;
this._shouldBeHiddenCache = new Set();
this._groupableMessages = new Map();
this._groupableMessageTitle = new Map();
this._shortcuts = new Map();
this._regexMatchRanges = [];
this._consoleContextSelector = new ConsoleContextSelector();
this._filterStatusText = new UI.Toolbar.ToolbarText();
this._filterStatusText.element.classList.add('dimmed');
this._showSettingsPaneSetting =
Common.Settings.Settings.instance().createSetting('consoleShowSettingsToolbar', false);
this._showSettingsPaneButton = new UI.Toolbar.ToolbarSettingToggle(
this._showSettingsPaneSetting, 'largeicon-settings-gear', i18nString(UIStrings.consoleSettings));
this._progressToolbarItem = new UI.Toolbar.ToolbarItem(document.createElement('div'));
this._groupSimilarSetting = Common.Settings.Settings.instance().moduleSetting('consoleGroupSimilar');
this._groupSimilarSetting.addChangeListener(() => this._updateMessageList());
const groupSimilarToggle = new UI.Toolbar.ToolbarSettingCheckbox(
this._groupSimilarSetting, i18nString(UIStrings.groupSimilarMessagesInConsole));
const toolbar = new UI.Toolbar.Toolbar('console-main-toolbar', this._consoleToolbarContainer);
const rightToolbar = new UI.Toolbar.Toolbar('', this._consoleToolbarContainer);
toolbar.appendToolbarItem(this._splitWidget.createShowHideSidebarButton(i18nString(UIStrings.consoleSidebar)));
toolbar.appendToolbarItem(UI.Toolbar.Toolbar.createActionButton(
(UI.ActionRegistry.ActionRegistry.instance().action('console.clear') as UI.ActionRegistration.Action)));
toolbar.appendSeparator();
toolbar.appendToolbarItem(this._consoleContextSelector.toolbarItem());
toolbar.appendSeparator();
const liveExpressionButton = UI.Toolbar.Toolbar.createActionButton(
(UI.ActionRegistry.ActionRegistry.instance().action('console.create-pin') as UI.ActionRegistration.Action));
toolbar.appendToolbarItem(liveExpressionButton);
toolbar.appendSeparator();
toolbar.appendToolbarItem(this._filter._textFilterUI);
toolbar.appendToolbarItem(this._filter._levelMenuButton);
toolbar.appendToolbarItem(this._progressToolbarItem);
toolbar.appendSeparator();
this._issuesCounter = new UIComponents.IconButton.IconButton();
this._issuesCounter.id = 'console-issues-counter';
const issuesToolbarItem = new UI.Toolbar.ToolbarItem(this._issuesCounter);
this._issuesCounter.data = {
clickHandler: (): void => {
Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.StatusBarIssuesCounter);
UI.ViewManager.ViewManager.instance().showView('issues-pane');
},
groups: [{iconName: 'issue-text-icon', iconColor: '#1a73e8'}],
};
toolbar.appendToolbarItem(issuesToolbarItem);
rightToolbar.appendSeparator();
rightToolbar.appendToolbarItem(this._filterStatusText);
rightToolbar.appendToolbarItem(this._showSettingsPaneButton);
this._preserveLogCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
Common.Settings.Settings.instance().moduleSetting('preserveConsoleLog'),
i18nString(UIStrings.doNotClearLogOnPageReload), i18nString(UIStrings.preserveLog));
this._hideNetworkMessagesCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
this._filter._hideNetworkMessagesSetting, this._filter._hideNetworkMessagesSetting.title(),
i18nString(UIStrings.hideNetwork));
const filterByExecutionContextCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
this._filter._filterByExecutionContextSetting, i18nString(UIStrings.onlyShowMessagesFromTheCurrentContext),
i18nString(UIStrings.selectedContextOnly));
const monitoringXHREnabledSetting = Common.Settings.Settings.instance().moduleSetting('monitoringXHREnabled');
this._timestampsSetting = Common.Settings.Settings.instance().moduleSetting('consoleTimestampsEnabled');
this._consoleHistoryAutocompleteSetting =
Common.Settings.Settings.instance().moduleSetting('consoleHistoryAutocomplete');
const settingsPane = new UI.Widget.HBox();
settingsPane.show(this._contentsElement);
settingsPane.element.classList.add('console-settings-pane');
UI.ARIAUtils.setAccessibleName(settingsPane.element, i18nString(UIStrings.consoleSettings));
UI.ARIAUtils.markAsGroup(settingsPane.element);
const settingsToolbarLeft = new UI.Toolbar.Toolbar('', settingsPane.element);
settingsToolbarLeft.makeVertical();
settingsToolbarLeft.appendToolbarItem(this._hideNetworkMessagesCheckbox);
settingsToolbarLeft.appendToolbarItem(this._preserveLogCheckbox);
settingsToolbarLeft.appendToolbarItem(filterByExecutionContextCheckbox);
settingsToolbarLeft.appendToolbarItem(groupSimilarToggle);
const settingsToolbarRight = new UI.Toolbar.Toolbar('', settingsPane.element);
settingsToolbarRight.makeVertical();
settingsToolbarRight.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(monitoringXHREnabledSetting));
const eagerEvalCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
Common.Settings.Settings.instance().moduleSetting('consoleEagerEval'),
i18nString(UIStrings.eagerlyEvaluateTextInThePrompt));
settingsToolbarRight.appendToolbarItem(eagerEvalCheckbox);
settingsToolbarRight.appendToolbarItem(
new UI.Toolbar.ToolbarSettingCheckbox(this._consoleHistoryAutocompleteSetting));
const userGestureCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
Common.Settings.Settings.instance().moduleSetting('consoleUserActivationEval'));
settingsToolbarRight.appendToolbarItem(userGestureCheckbox);
if (!this._showSettingsPaneSetting.get()) {
settingsPane.element.classList.add('hidden');
}
this._showSettingsPaneSetting.addChangeListener(
() => settingsPane.element.classList.toggle('hidden', !this._showSettingsPaneSetting.get()));
this._pinPane = new ConsolePinPane(liveExpressionButton);
this._pinPane.element.classList.add('console-view-pinpane');
this._pinPane.show(this._contentsElement);
this._pinPane.element.addEventListener('keydown', event => {
if ((event.key === 'Enter' &&
UI.KeyboardShortcut.KeyboardShortcut.eventHasCtrlOrMeta((event as KeyboardEvent))) ||
event.keyCode === UI.KeyboardShortcut.Keys.Esc.code) {
this._prompt.focus();
event.consume();
}
});
this._viewport = new ConsoleViewport(this);
this._viewport.setStickToBottom(true);
this._viewport.contentElement().classList.add('console-group', 'console-group-messages');
this._contentsElement.appendChild(this._viewport.element);
this._messagesElement = this._viewport.element;
this._messagesElement.id = 'console-messages';
this._messagesElement.classList.add('monospace');
this._messagesElement.addEventListener('click', this._messagesClicked.bind(this), false);
this._messagesElement.addEventListener('paste', this._messagesPasted.bind(this), true);
this._messagesElement.addEventListener('clipboard-paste', this._messagesPasted.bind(this), true);
UI.ARIAUtils.markAsGroup(this._messagesElement);
this._viewportThrottler = new Common.Throttler.Throttler(50);
this._pendingBatchResize = false;
this._onMessageResizedBound = (e: Common.EventTarget.EventTargetEvent): void => {
this._onMessageResized(e);
};
this._topGroup = ConsoleGroup.createTopGroup();
this._currentGroup = this._topGroup;
this._promptElement = this._messagesElement.createChild('div', 'source-code');
this._promptElement.id = 'console-prompt';
// FIXME: This is a workaround for the selection machinery bug. See crbug.com/410899
const selectAllFixer = this._messagesElement.createChild('div', 'console-view-fix-select-all');
selectAllFixer.textContent = '.';
UI.ARIAUtils.markAsHidden(selectAllFixer);
this._registerShortcuts();
this._messagesElement.addEventListener('contextmenu', this._handleContextMenuEvent.bind(this), false);
this._linkifier = new Components.Linkifier.Linkifier(MaxLengthForLinks);
this._consoleMessages = [];
this._viewMessageSymbol = Symbol('viewMessage');
this._consoleHistorySetting = Common.Settings.Settings.instance().createLocalSetting('consoleHistory', []);
this._prompt = new ConsolePrompt();
this._prompt.show(this._promptElement);
this._prompt.element.addEventListener('keydown', this._promptKeyDown.bind(this), true);
this._prompt.addEventListener(ConsolePromptEvents.TextChanged, this._promptTextChanged, this);
this._messagesElement.addEventListener('keydown', this._messagesKeyDown.bind(this), false);
this._prompt.element.addEventListener('focusin', () => {
if (this._isScrolledToBottom()) {
this._viewport.setStickToBottom(true);
}
});
this._consoleHistoryAutocompleteSetting.addChangeListener(this._consoleHistoryAutocompleteChanged, this);
const historyData = this._consoleHistorySetting.get();
this._prompt.history().setHistoryData(historyData);
this._consoleHistoryAutocompleteChanged();
this._updateFilterStatus();
this._timestampsSetting.addChangeListener(this._consoleTimestampsSettingChanged, this);
this._registerWithMessageSink();
UI.Context.Context.instance().addFlavorChangeListener(
SDK.RuntimeModel.ExecutionContext, this._executionContextChanged, this);
this._messagesElement.addEventListener(
'mousedown', (event: Event) => this._updateStickToBottomOnPointerDown((event as MouseEvent).button === 2),
false);
this._messagesElement.addEventListener('mouseup', this._updateStickToBottomOnPointerUp.bind(this), false);
this._messagesElement.addEventListener('mouseleave', this._updateStickToBottomOnPointerUp.bind(this), false);
this._messagesElement.addEventListener('wheel', this._updateStickToBottomOnWheel.bind(this), false);
this._messagesElement.addEventListener(
'touchstart', this._updateStickToBottomOnPointerDown.bind(this, false), false);
this._messagesElement.addEventListener('touchend', this._updateStickToBottomOnPointerUp.bind(this), false);
this._messagesElement.addEventListener('touchcancel', this._updateStickToBottomOnPointerUp.bind(this), false);
SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
SDK.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this);
SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
SDK.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
SDK.ConsoleModel.ConsoleModel.instance().messages().forEach(this._addConsoleMessage, this);
this._issueMessage = undefined;
const issuesManager = BrowserSDK.IssuesManager.IssuesManager.instance();
issuesManager.addEventListener(
BrowserSDK.IssuesManager.Events.IssuesCountUpdated, this._updateIssuesToolbarItem, this);
}
_onIssuesCountChanged(): void {
if (BrowserSDK.IssuesManager.IssuesManager.instance().numberOfIssues() === 0) {
if (this._issueMessage) {
this._issueMessage.element().remove();
this._issueMessage = undefined;
this._scheduleViewportRefresh();
}
} else if (!this._issueMessage) {
this._issueMessage = new IssueMessage();
this._scheduleViewportRefresh();
}
}
static instance(): ConsoleView {
if (!consoleViewInstance) {
consoleViewInstance = new ConsoleView();
}
return consoleViewInstance;
}
static clearConsole(): void {
const consoleView = ConsoleView.instance();
if (consoleView._issueMessage) {
consoleView._issueMessage.element().remove();
consoleView._issueMessage = undefined;
consoleView._scheduleViewportRefresh();
}
SDK.ConsoleModel.ConsoleModel.instance().requestClearMessages();
}
_onFilterChanged(): void {
this._filter._currentFilter.levelsMask =
this._isSidebarOpen ? ConsoleFilter.allLevelsFilterValue() : this._filter._messageLevelFiltersSetting.get();
this._cancelBuildHiddenCache();
if (this._immediatelyFilterMessagesForTest) {
for (const viewMessage of this._consoleMessages) {
this._computeShouldMessageBeVisible(viewMessage);
}
this._updateMessageList();
return;
}
this._buildHiddenCache(0, this._consoleMessages.slice());
}
_setImmediatelyFilterMessagesForTest(): void {
this._immediatelyFilterMessagesForTest = true;
}
searchableView(): UI.SearchableView.SearchableView {
return this._searchableView;
}
_clearHistory(): void {
this._consoleHistorySetting.set([]);
this._prompt.history().setHistoryData([]);
}
_consoleHistoryAutocompleteChanged(): void {
this._prompt.setAddCompletionsFromHistory(this._consoleHistoryAutocompleteSetting.get());
}
itemCount(): number {
if (this._issueMessage) {
return this._visibleViewMessages.length + 1;
}
return this._visibleViewMessages.length;
}
itemElement(index: number): ConsoleViewportElement|null {
const issueMessage = this._issueMessage;
if (issueMessage) {
if (index === 0) {
return issueMessage;
}
return this._visibleViewMessages[index - 1];
}
return this._visibleViewMessages[index];
}
fastHeight(index: number): number {
const issueMessage = this._issueMessage;
if (issueMessage) {
if (index === 0) {
return issueMessage.fastHeight() || 37;
}
return this._visibleViewMessages[index - 1].fastHeight();
}
return this._visibleViewMessages[index].fastHeight();
}
minimumRowHeight(): number {
return 16;
}
_registerWithMessageSink(): void {
Common.Console.Console.instance().messages().forEach(this._addSinkMessage, this);
Common.Console.Console.instance().addEventListener(Common.Console.Events.MessageAdded, messageAdded, this);
function messageAdded(this: ConsoleView, event: Common.EventTarget.EventTargetEvent): void {
this._addSinkMessage((event.data as Common.Console.Message));
}
}
_addSinkMessage(message: Common.Console.Message): void {
let level: Protocol.Log.LogEntryLevel = SDK.ConsoleModel.MessageLevel.Verbose;
switch (message.level) {
case Common.Console.MessageLevel.Info:
level = SDK.ConsoleModel.MessageLevel.Info;
break;
case Common.Console.MessageLevel.Error:
level = SDK.ConsoleModel.MessageLevel.Error;
break;
case Common.Console.MessageLevel.Warning:
level = SDK.ConsoleModel.MessageLevel.Warning;
break;
}
const consoleMessage = new SDK.ConsoleModel.ConsoleMessage(
null, SDK.ConsoleModel.MessageSource.Other, level, message.text, SDK.ConsoleModel.MessageType.System, undefined,
undefined, undefined, undefined, undefined, message.timestamp);
this._addConsoleMessage(consoleMessage);
}
_consoleTimestampsSettingChanged(): void {
this._updateMessageList();
this._consoleMessages.forEach(viewMessage => viewMessage.updateTimestamp());
this._groupableMessageTitle.forEach(viewMessage => viewMessage.updateTimestamp());
}
_executionContextChanged(): void {
this._prompt.clearAutocomplete();
}
willHide(): void {
this._hidePromptSuggestBox();
}
wasShown(): void {
this._updateIssuesToolbarItem();
this._viewport.refresh();
}
focus(): void {
if (this._viewport.hasVirtualSelection()) {
(this._viewport.contentElement() as HTMLElement).focus();
} else {
this._focusPrompt();
}
}
_focusPrompt(): void {
if (!this._prompt.hasFocus()) {
const oldStickToBottom = this._viewport.stickToBottom();
const oldScrollTop = this._viewport.element.scrollTop;
this._prompt.focus();
this._viewport.setStickToBottom(oldStickToBottom);
this._viewport.element.scrollTop = oldScrollTop;
}
}
restoreScrollPositions(): void {
if (this._viewport.stickToBottom()) {
this._immediatelyScrollToBottom();
} else {
super.restoreScrollPositions();
}
}
onResize(): void {
this._scheduleViewportRefresh();
this._hidePromptSuggestBox();
if (this._viewport.stickToBottom()) {
this._immediatelyScrollToBottom();
}
for (let i = 0; i < this._visibleViewMessages.length; ++i) {
this._visibleViewMessages[i].onResize();
}
}
_hidePromptSuggestBox(): void {
this._prompt.clearAutocomplete();
}
async _invalidateViewport(): Promise<void> {
this._updateIssuesToolbarItem();
if (this._muteViewportUpdates) {
this._maybeDirtyWhileMuted = true;
return;
}
if (this._needsFullUpdate) {
this._updateMessageList();
delete this._needsFullUpdate;
} else {
this._viewport.invalidate();
}
return;
}
_updateIssuesToolbarItem(): void {
const issueCount = BrowserSDK.IssuesManager.IssuesManager.instance().numberOfIssues();
let issuesSummary = '';
let issuesTitleGotoIssues = '';
if (issueCount === 0) {
issuesSummary = i18nString(UIStrings.noIssue);
issuesTitleGotoIssues = i18nString(UIStrings.issueToolbarTooltipHaveNoIssues);
} else if (issueCount === 1) {
issuesSummary = i18nString(UIStrings.oneIssue);
issuesTitleGotoIssues = i18nString(UIStrings.issueToolbarTooltipHaveOneIssues);
} else {
issuesSummary = i18nString(UIStrings.multipleIssues, {issueCount});
issuesTitleGotoIssues = i18nString(UIStrings.issueToolbarTooltipHaveMultipleIssues, {issueCount});
}
this._issuesCounter.setTexts([issuesSummary]);
const issuesTitleGeneral = i18nString(UIStrings.issueToolbarTooltipGeneral);
const issuesTitle = `${issuesTitleGeneral} ${issuesTitleGotoIssues}`;
UI.Tooltip.Tooltip.install(this._issuesCounter, issuesTitle);
UI.ARIAUtils.setAccessibleName(this._issuesCounter, issuesTitle);
}
_scheduleViewportRefresh(): void {
if (this._muteViewportUpdates) {
this._maybeDirtyWhileMuted = true;
this._scheduleViewportRefreshForTest(true);
return;
}
this._scheduleViewportRefreshForTest(false);
this._scheduledRefreshPromiseForTest = this._viewportThrottler.schedule(this._invalidateViewport.bind(this));
}
_scheduleViewportRefreshForTest(_muted: boolean): void {
// This functions is sniffed in tests.
}
_immediatelyScrollToBottom(): void {
// This will scroll viewport and trigger its refresh.
this._viewport.setStickToBottom(true);
this._promptElement.scrollIntoView(true);
}
_updateFilterStatus(): void {
if (this._hiddenByFilterCount === this._lastShownHiddenByFilterCount) {
return;
}
this._filterStatusText.setText(i18nString(UIStrings.sHidden, {PH1: this._hiddenByFilterCount}));
this._filterStatusText.setVisible(Boolean(this._hiddenByFilterCount));
this._lastShownHiddenByFilterCount = this._hiddenByFilterCount;
}
_onConsoleMessageAdded(event: Common.EventTarget.EventTargetEvent): void {
const message = (event.data as SDK.ConsoleModel.ConsoleMessage);
this._addConsoleMessage(message);
}
_addConsoleMessage(message: SDK.ConsoleModel.ConsoleMessage): void {
const viewMessage = this._createViewMessage(message);
consoleMessageToViewMessage.set(message, viewMessage);
if (message.type === SDK.ConsoleModel.MessageType.Command || message.type === SDK.ConsoleModel.MessageType.Result) {
const lastMessage = this._consoleMessages[this._consoleMessages.length - 1];
const newTimestamp = lastMessage && messagesSortedBySymbol.get(lastMessage) || 0;
messagesSortedBySymbol.set(viewMessage, newTimestamp);
} else {
messagesSortedBySymbol.set(viewMessage, viewMessage.consoleMessage().timestamp);
}
let insertAt;
if (!this._consoleMessages.length ||
timeComparator(viewMessage, this._consoleMessages[this._consoleMessages.length - 1]) > 0) {
insertAt = this._consoleMessages.length;
} else {
insertAt = Platform.ArrayUtilities.upperBound(this._consoleMessages, viewMessage, timeComparator);
}
const insertedInMiddle = insertAt < this._consoleMessages.length;
this._consoleMessages.splice(insertAt, 0, viewMessage);
this._filter.onMessageAdded(message);
this._sidebar.onMessageAdded(viewMessage);
// If we already have similar messages, go slow path.
let shouldGoIntoGroup = false;
const shouldGroupSimilar = this._groupSimilarSetting.get();
if (message.isGroupable()) {
const groupKey = viewMessage.groupKey();
shouldGoIntoGroup = shouldGroupSimilar && this._groupableMessages.has(groupKey);
let list = this._groupableMessages.get(groupKey);
if (!list) {
list = [];
this._groupableMessages.set(groupKey, list);
}
list.push(viewMessage);
}
this._computeShouldMessageBeVisible(viewMessage);
if (!shouldGoIntoGroup && !insertedInMiddle) {
this._appendMessageToEnd(
viewMessage,
!shouldGroupSimilar /* crbug.com/1082963: prevent collapse of same messages when "Group similar" is false */);
this._updateFilterStatus();
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
} else {
this._needsFullUpdate = true;
}
this._scheduleViewportRefresh();
this._consoleMessageAddedForTest(viewMessage);
function timeComparator(viewMessage1: ConsoleViewMessage, viewMessage2: ConsoleViewMessage): number {
return (messagesSortedBySymbol.get(viewMessage1) || 0) - (messagesSortedBySymbol.get(viewMessage2) || 0);
}
}
_onConsoleMessageUpdated(event: Common.EventTarget.EventTargetEvent): void {
const message = (event.data as SDK.ConsoleModel.ConsoleMessage);
const viewMessage = consoleMessageToViewMessage.get(message);
if (viewMessage) {
viewMessage.updateMessageElement();
this._computeShouldMessageBeVisible(viewMessage);
this._updateMessageList();
}
}
_consoleMessageAddedForTest(_viewMessage: ConsoleViewMessage): void {
}
_shouldMessageBeVisible(viewMessage: ConsoleViewMessage): boolean {
return !this._shouldBeHiddenCache.has(viewMessage);
}
_computeShouldMessageBeVisible(viewMessage: ConsoleViewMessage): void {
if (this._filter.shouldBeVisible(viewMessage) &&
(!this._isSidebarOpen || this._sidebar.shouldBeVisible(viewMessage))) {
this._shouldBeHiddenCache.delete(viewMessage);
} else {
this._shouldBeHiddenCache.add(viewMessage);
}
}
_appendMessageToEnd(viewMessage: ConsoleViewMessage, preventCollapse?: boolean): void {
if (!this._shouldMessageBeVisible(viewMessage)) {
this._hiddenByFilterCount++;
return;
}
if (!preventCollapse &&
this._tryToCollapseMessages(viewMessage, this._visibleViewMessages[this._visibleViewMessages.length - 1])) {
return;
}
const lastMessage = this._visibleViewMessages[this._visibleViewMessages.length - 1];
if (viewMessage.consoleMessage().type === SDK.ConsoleModel.MessageType.EndGroup) {
if (lastMessage && !this._currentGroup.messagesHidden()) {
lastMessage.incrementCloseGroupDecorationCount();
}
this._currentGroup = this._currentGroup.parentGroup() || this._currentGroup;
return;
}
if (!this._currentGroup.messagesHidden()) {
const originatingMessage = viewMessage.consoleMessage().originatingMessage();
if (lastMessage && originatingMessage && lastMessage.consoleMessage() === originatingMessage) {
viewMessage.toMessageElement().classList.add('console-adjacent-user-command-result');
}
this._visibleViewMessages.push(viewMessage);
this._searchMessage(this._visibleViewMessages.length - 1);
}
if (viewMessage.consoleMessage().isGroupStartMessage()) {
this._currentGroup = new ConsoleGroup(this._currentGroup, (viewMessage as ConsoleGroupViewMessage));
}
this._messageAppendedForTests();
}
_messageAppendedForTests(): void {
// This method is sniffed in tests.
}
_createViewMessage(message: SDK.ConsoleModel.ConsoleMessage): ConsoleViewMessage {
const nestingLevel = this._currentGroup.nestingLevel();
switch (message.type) {
case SDK.ConsoleModel.MessageType.Command:
return new ConsoleCommand(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
case SDK.ConsoleModel.MessageType.Result:
return new ConsoleCommandResult(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
case SDK.ConsoleModel.MessageType.StartGroupCollapsed:
case SDK.ConsoleModel.MessageType.StartGroup:
return new ConsoleGroupViewMessage(
message, this._linkifier, nestingLevel, this._updateMessageList.bind(this), this._onMessageResizedBound);
case SDK.ConsoleModel.MessageType.Table:
return new ConsoleTableMessageView(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
default:
return new ConsoleViewMessage(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
}
}
async _onMessageResized(event: Common.EventTarget.EventTargetEvent): Promise<void> {
const treeElement = (event.data as UI.TreeOutline.TreeElement);
if (this._pendingBatchResize || !treeElement.treeOutline) {
return;
}
this._pendingBatchResize = true;
await Promise.resolve();
const treeOutlineElement = treeElement.treeOutline.element;
this._viewport.setStickToBottom(this._isScrolledToBottom());
// Scroll, in case mutations moved the element below the visible area.
if (treeOutlineElement.offsetHeight <= this._messagesElement.offsetHeight) {
treeOutlineElement.scrollIntoViewIfNeeded();
}
this._pendingBatchResize = false;
}
_consoleCleared(): void {
const hadFocus = this._viewport.element.hasFocus();
this._cancelBuildHiddenCache();
this._currentMatchRangeIndex = -1;
this._consoleMessages = [];
this._groupableMessages.clear();
this._groupableMessageTitle.clear();
this._sidebar.clear();
this._updateMessageList();
this._hidePromptSuggestBox();
this._viewport.setStickToBottom(true);
this._linkifier.reset();
this._filter.clear();
if (hadFocus) {
this._prompt.focus();
}
UI.ARIAUtils.alert(i18nString(UIStrings.consoleCleared), this._viewport.element);
}
_handleContextMenuEvent(event: Event): void {
const contextMenu = new UI.ContextMenu.ContextMenu(event);
const eventTarget = (event.target as Node);
if (eventTarget.isSelfOrDescendant(this._promptElement)) {
contextMenu.show();
return;
}
const sourceElement = eventTarget.enclosingNodeOrSelfWithClass('console-message-wrapper');
const consoleViewMessage = sourceElement && getMessageForElement(sourceElement);
const consoleMessage = consoleViewMessage ? consoleViewMessage.consoleMessage() : null;
if (consoleMessage && consoleMessage.url) {
const menuTitle = i18nString(
UIStrings.hideMessagesFromS, {PH1: new Common.ParsedURL.ParsedURL(consoleMessage.url).displayName});
contextMenu.headerSection().appendItem(
menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
}
contextMenu.defaultSection().appendAction('console.clear');
contextMenu.defaultSection().appendAction('console.clear.history');
contextMenu.saveSection().appendItem(i18nString(UIStrings.saveAs), this._saveConsole.bind(this));
if (this.element.hasSelection()) {
contextMenu.clipboardSection().appendItem(
i18nString(UIStrings.copyVisibleStyledSelection), this._viewport.copyWithStyles.bind(this._viewport));
}
if (consoleMessage) {
const request = SDK.NetworkLog.NetworkLog.requestForConsoleMessage(consoleMessage);
if (request && SDK.NetworkManager.NetworkManager.canReplayRequest(request)) {
contextMenu.debugSection().appendItem(
i18nString(UIStrings.replayXhr), SDK.NetworkManager.NetworkManager.replayRequest.bind(null, request));
}
}
contextMenu.show();
}
async _saveConsole(): Promise<void> {
const url = (SDK.SDKModel.TargetManager.instance().mainTarget() as SDK.SDKModel.Target).inspectedURL();
const parsedURL = Common.ParsedURL.ParsedURL.fromString(url);
const filename = Platform.StringUtilities.sprintf('%s-%d.log', parsedURL ? parsedURL.host : 'console', Date.now());
const stream = new Bindings.FileUtils.FileOutputStream();
const progressIndicator = new UI.ProgressIndicator.ProgressIndicator();
progressIndicator.setTitle(i18nString(UIStrings.writingFile));
progressIndicator.setTotalWork(this.itemCount());
const chunkSize = 350;
if (!await stream.open(filename)) {
return;
}
this._progressToolbarItem.element.appendChild(progressIndicator.element);
let messageIndex = 0;
while (messageIndex < this.itemCount() && !progressIndicator.isCanceled()) {
const messageContents = [];
let i;
for (i = 0; i < chunkSize && i + messageIndex < this.itemCount(); ++i) {
const message = (this.itemElement(messageIndex + i) as ConsoleViewMessage);
messageContents.push(message.toExportString());
}
messageIndex += i;
await stream.write(messageContents.join('\n') + '\n');
progressIndicator.setWorked(messageIndex);
}
stream.close();
progressIndicator.done();
}
_tryToCollapseMessages(viewMessage: ConsoleViewMessage, lastMessage?: ConsoleViewMessage): boolean {
const timestampsShown = this._timestampsSetting.get();
if (!timestampsShown && lastMessage && !viewMessage.consoleMessage().isGroupMessage() &&
viewMessage.consoleMessage().type !== SDK.ConsoleModel.MessageType.Command &&
viewMessage.consoleMessage().type !== SDK.ConsoleModel.MessageType.Result &&
viewMessage.consoleMessage().isEqual(lastMessage.consoleMessage())) {
lastMessage.incrementRepeatCount();
if (viewMessage.isLastInSimilarGroup()) {
lastMessage.setInSimilarGroup(true, true);
}
return true;
}
return false;
}
_buildHiddenCache(startIndex: number, viewMessages: ConsoleViewMessage[]): void {
const startTime = Date.now();
let i;
for (i = startIndex; i < viewMessages.length; ++i) {
this._computeShouldMessageBeVisible(viewMessages[i]);
if (i % 10 === 0 && Date.now() - startTime > 12) {
break;
}
}
if (i === viewMessages.length) {
this._updateMessageList();
return;
}
this._buildHiddenCacheTimeout =
this.element.window().requestAnimationFrame(this._buildHiddenCache.bind(this, i, viewMessages));
}
_cancelBuildHiddenCache(): void {
this._shouldBeHiddenCache.clear();
if (this._buildHiddenCacheTimeout) {
this.element.window().cancelAnimationFrame(this._buildHiddenCacheTimeout);
delete this._buildHiddenCacheTimeout;
}
}
_updateMessageList(): void {
this._topGroup = ConsoleGroup.createTopGroup();
this._currentGroup = this._topGroup;
this._regexMatchRanges = [];
this._hiddenByFilterCount = 0;
for (let i = 0; i < this._visibleViewMessages.length; ++i) {
this._visibleViewMessages[i].resetCloseGroupDecorationCount();
this._visibleViewMessages[i].resetIncrementRepeatCount();
}
this._visibleViewMessages = [];
if (this._groupSimilarSetting.get()) {
this._addGroupableMessagesToEnd();
} else {
for (let i = 0; i < this._consoleMessages.length; ++i) {
this._consoleMessages[i].setInSimilarGroup(false);
this._appendMessageToEnd(
this._consoleMessages[i],
true /* crbug.com/1082963: prevent collapse of same messages when "Group similar" is false */);
}
}
this._updateFilterStatus();
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
this._viewport.invalidate();
}
_addGroupableMessagesToEnd(): void {
const alreadyAdded = new Set<SDK.ConsoleModel.ConsoleMessage>();
const processedGroupKeys = new Set<string>();
for (let i = 0; i < this._consoleMessages.length; ++i) {
const viewMessage = this._consoleMessages[i];
const message = viewMessage.consoleMessage();
if (alreadyAdded.has(message)) {
continue;
}
if (!message.isGroupable()) {
this._appendMessageToEnd(viewMessage);
alreadyAdded.add(message);
continue;
}
const key = viewMessage.groupKey();
const viewMessagesInGroup = this._groupableMessages.get(key);
if (!viewMessagesInGroup || viewMessagesInGroup.length < 5) {
viewMessage.setInSimilarGroup(false);
this._appendMessageToEnd(viewMessage);
alreadyAdded.add(message);
continue;
}
if (processedGroupKeys.has(key)) {
continue;
}
if (!viewMessagesInGroup.find(x => this._shouldMessageBeVisible(x))) {
// Optimize for speed.
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
// @ts-expect-error
Platform.SetUtilities.addAll(alreadyAdded, viewMessagesInGroup);
processedGroupKeys.add(key);
continue;
}
// Create artificial group start and end messages.
let startGroupViewMessage = this._groupableMessageTitle.get(key);
if (!startGroupViewMessage) {
const startGroupMessage = new SDK.ConsoleModel.ConsoleMessage(
null, message.source, message.level, viewMessage.groupTitle(),
SDK.ConsoleModel.MessageType.StartGroupCollapsed);
startGroupViewMessage = this._createViewMessage(startGroupMessage);
this._groupableMessageTitle.set(key, startGroupViewMessage);
}
startGroupViewMessage.setRepeatCount(viewMessagesInGroup.length);
this._appendMessageToEnd(startGroupViewMessage);
for (const viewMessageInGroup of viewMessagesInGroup) {
viewMessageInGroup.setInSimilarGroup(
true, viewMessagesInGroup[viewMessagesInGroup.length - 1] === viewMessageInGroup);
this._appendMessageToEnd(viewMessageInGroup, true);
alreadyAdded.add(viewMessageInGroup.consoleMessage());
}
const endGroupMessage = new SDK.ConsoleModel.ConsoleMessage(
null, message.source, message.level, message.messageText, SDK.ConsoleModel.MessageType.EndGroup);
this._appendMessageToEnd(this._createViewMessage(endGroupMessage));
}
}
_messagesClicked(event: Event): void {
const target = (event.target as Node | null);
// Do not focus prompt if messages have selection.
if (!this._messagesElement.hasSelection()) {
const clickedOutsideMessageList =
target === this._messagesElement || this._prompt.belowEditorElement().isSelfOrAncestor(target);
if (clickedOutsideMessageList) {
this._prompt.moveCaretToEndOfPrompt();
this._focus