blob: 1fefa396fd742db5ea463dcdb58b7e3fd15166ba [file] [log] [blame]
/*
* 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.
*/
/**
* @implements {UI.Searchable}
* @implements {Console.ConsoleViewportProvider}
* @unrestricted
*/
export default class ConsoleView extends UI.VBox {
constructor() {
super();
this.setMinimumSize(0, 35);
this.registerRequiredCSS('console/consoleView.css');
this.registerRequiredCSS('object_ui/objectValue.css');
this._searchableView = new UI.SearchableView(this);
this._searchableView.element.classList.add('console-searchable-view');
this._searchableView.setPlaceholder(Common.UIString('Find string in logs'));
this._searchableView.setMinimalSearchQuerySize(0);
this._sidebar = new Console.ConsoleSidebar();
this._sidebar.addEventListener(Console.ConsoleSidebar.Events.FilterSelected, this._onFilterChanged.bind(this));
this._isSidebarOpen = false;
this._filter = new ConsoleViewFilter(this._onFilterChanged.bind(this));
const consoleToolbarContainer = this.element.createChild('div', 'console-toolbar-container');
this._splitWidget =
new UI.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;
if (this._isSidebarOpen) {
this._filter._levelMenuButton.setEnabled(false);
}
this._splitWidget.addEventListener(UI.SplitWidget.Events.ShowModeChanged, event => {
this._isSidebarOpen = event.data === UI.SplitWidget.ShowMode.Both;
this._filter._levelMenuButton.setEnabled(!this._isSidebarOpen);
this._onFilterChanged();
});
this._contentsElement = this._searchableView.element;
this.element.classList.add('console-view');
/** @type {!Array.<!Console.ConsoleViewMessage>} */
this._visibleViewMessages = [];
this._hiddenByFilterCount = 0;
/** @type {!Set<!Console.ConsoleViewMessage>} */
this._shouldBeHiddenCache = new Set();
/** @type {!Map<string, !Array<!Console.ConsoleViewMessage>>} */
this._groupableMessages = new Map();
/** @type {!Map<string, !Console.ConsoleViewMessage>} */
this._groupableMessageTitle = new Map();
/**
* @type {!Array.<!Console.ConsoleView.RegexMatchRange>}
*/
this._regexMatchRanges = [];
this._consoleContextSelector = new Console.ConsoleContextSelector();
this._filterStatusText = new UI.ToolbarText();
this._filterStatusText.element.classList.add('dimmed');
this._showSettingsPaneSetting = Common.settings.createSetting('consoleShowSettingsToolbar', false);
this._showSettingsPaneButton = new UI.ToolbarSettingToggle(
this._showSettingsPaneSetting, 'largeicon-settings-gear', Common.UIString('Console settings'));
this._progressToolbarItem = new UI.ToolbarItem(createElement('div'));
this._groupSimilarSetting = Common.settings.moduleSetting('consoleGroupSimilar');
this._groupSimilarSetting.addChangeListener(() => this._updateMessageList());
const groupSimilarToggle =
new UI.ToolbarSettingCheckbox(this._groupSimilarSetting, Common.UIString('Group similar'));
const toolbar = new UI.Toolbar('console-main-toolbar', consoleToolbarContainer);
const rightToolbar = new UI.Toolbar('', consoleToolbarContainer);
toolbar.appendToolbarItem(this._splitWidget.createShowHideSidebarButton(ls`console sidebar`));
toolbar.appendToolbarItem(UI.Toolbar.createActionButton(
/** @type {!UI.Action }*/ (UI.actionRegistry.action('console.clear'))));
toolbar.appendSeparator();
toolbar.appendToolbarItem(this._consoleContextSelector.toolbarItem());
toolbar.appendSeparator();
const liveExpressionButton =
UI.Toolbar.createActionButton(/** @type {!UI.Action }*/ (UI.actionRegistry.action('console.create-pin')));
toolbar.appendToolbarItem(liveExpressionButton);
toolbar.appendSeparator();
toolbar.appendToolbarItem(this._filter._textFilterUI);
toolbar.appendToolbarItem(this._filter._levelMenuButton);
toolbar.appendToolbarItem(this._progressToolbarItem);
rightToolbar.appendSeparator();
rightToolbar.appendToolbarItem(this._filterStatusText);
rightToolbar.appendToolbarItem(this._showSettingsPaneButton);
this._preserveLogCheckbox = new UI.ToolbarSettingCheckbox(
Common.moduleSetting('preserveConsoleLog'), Common.UIString('Do not clear log on page reload / navigation'),
Common.UIString('Preserve log'));
this._hideNetworkMessagesCheckbox = new UI.ToolbarSettingCheckbox(
this._filter._hideNetworkMessagesSetting, this._filter._hideNetworkMessagesSetting.title(),
Common.UIString('Hide network'));
const filterByExecutionContextCheckbox = new UI.ToolbarSettingCheckbox(
this._filter._filterByExecutionContextSetting,
Common.UIString('Only show messages from the current context (top, iframe, worker, extension)'),
Common.UIString('Selected context only'));
const monitoringXHREnabledSetting = Common.moduleSetting('monitoringXHREnabled');
this._timestampsSetting = Common.moduleSetting('consoleTimestampsEnabled');
this._consoleHistoryAutocompleteSetting = Common.moduleSetting('consoleHistoryAutocomplete');
const settingsPane = new UI.HBox();
settingsPane.show(this._contentsElement);
settingsPane.element.classList.add('console-settings-pane');
UI.ARIAUtils.setAccessibleName(settingsPane.element, ls`Console settings`);
UI.ARIAUtils.markAsGroup(settingsPane.element);
const settingsToolbarLeft = new UI.Toolbar('', settingsPane.element);
settingsToolbarLeft.makeVertical();
settingsToolbarLeft.appendToolbarItem(this._hideNetworkMessagesCheckbox);
settingsToolbarLeft.appendToolbarItem(this._preserveLogCheckbox);
settingsToolbarLeft.appendToolbarItem(filterByExecutionContextCheckbox);
settingsToolbarLeft.appendToolbarItem(groupSimilarToggle);
const settingsToolbarRight = new UI.Toolbar('', settingsPane.element);
settingsToolbarRight.makeVertical();
settingsToolbarRight.appendToolbarItem(new UI.ToolbarSettingCheckbox(monitoringXHREnabledSetting));
const eagerEvalCheckbox = new UI.ToolbarSettingCheckbox(
Common.settings.moduleSetting('consoleEagerEval'), ls`Eagerly evaluate text in the prompt`);
settingsToolbarRight.appendToolbarItem(eagerEvalCheckbox);
settingsToolbarRight.appendToolbarItem(new UI.ToolbarSettingCheckbox(this._consoleHistoryAutocompleteSetting));
const userGestureCheckbox =
new UI.ToolbarSettingCheckbox(Common.settings.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 Console.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.eventHasCtrlOrMeta(/** @type {!KeyboardEvent} */ (event))) ||
event.keyCode === UI.KeyboardShortcut.Keys.Esc.code) {
this._prompt.focus();
event.consume();
}
});
this._viewport = new Console.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(50);
this._pendingBatchResize = false;
this._onMessageResizedBound = this._onMessageResized.bind(this);
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(Console.ConsoleViewMessage.MaxLengthForLinks);
/** @type {!Array.<!Console.ConsoleViewMessage>} */
this._consoleMessages = [];
this._viewMessageSymbol = Symbol('viewMessage');
this._consoleHistorySetting = Common.settings.createLocalSetting('consoleHistory', []);
this._prompt = new Console.ConsolePrompt();
this._prompt.show(this._promptElement);
this._prompt.element.addEventListener('keydown', this._promptKeyDown.bind(this), true);
this._prompt.addEventListener(Console.ConsolePrompt.Events.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.addFlavorChangeListener(SDK.ExecutionContext, this._executionContextChanged, this);
this._messagesElement.addEventListener(
'mousedown', event => this._updateStickToBottomOnPointerDown(event.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.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this);
SDK.consoleModel.addEventListener(SDK.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
SDK.consoleModel.messages().forEach(this._addConsoleMessage, this);
}
/**
* @return {!Console.ConsoleView}
*/
static instance() {
if (!ConsoleView._instance) {
ConsoleView._instance = new ConsoleView();
}
return ConsoleView._instance;
}
static clearConsole() {
SDK.consoleModel.requestClearMessages();
}
_onFilterChanged() {
this._filter._currentFilter.levelsMask = this._isSidebarOpen ? Console.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() {
this._immediatelyFilterMessagesForTest = true;
}
/**
* @return {!UI.SearchableView}
*/
searchableView() {
return this._searchableView;
}
_clearHistory() {
this._consoleHistorySetting.set([]);
this._prompt.history().setHistoryData([]);
}
_consoleHistoryAutocompleteChanged() {
this._prompt.setAddCompletionsFromHistory(this._consoleHistoryAutocompleteSetting.get());
}
/**
* @override
* @return {number}
*/
itemCount() {
return this._visibleViewMessages.length;
}
/**
* @override
* @param {number} index
* @return {?Console.ConsoleViewportElement}
*/
itemElement(index) {
return this._visibleViewMessages[index];
}
/**
* @override
* @param {number} index
* @return {number}
*/
fastHeight(index) {
return this._visibleViewMessages[index].fastHeight();
}
/**
* @override
* @return {number}
*/
minimumRowHeight() {
return 16;
}
_registerWithMessageSink() {
Common.console.messages().forEach(this._addSinkMessage, this);
Common.console.addEventListener(Common.Console.Events.MessageAdded, messageAdded, this);
/**
* @param {!Common.Event} event
* @this {Console.ConsoleView}
*/
function messageAdded(event) {
this._addSinkMessage(/** @type {!Common.Console.Message} */ (event.data));
}
}
/**
* @param {!Common.Console.Message} message
*/
_addSinkMessage(message) {
let level = SDK.ConsoleMessage.MessageLevel.Verbose;
switch (message.level) {
case Common.Console.MessageLevel.Info:
level = SDK.ConsoleMessage.MessageLevel.Info;
break;
case Common.Console.MessageLevel.Error:
level = SDK.ConsoleMessage.MessageLevel.Error;
break;
case Common.Console.MessageLevel.Warning:
level = SDK.ConsoleMessage.MessageLevel.Warning;
break;
}
const consoleMessage = new SDK.ConsoleMessage(
null, SDK.ConsoleMessage.MessageSource.Other, level, message.text, SDK.ConsoleMessage.MessageType.System,
undefined, undefined, undefined, undefined, undefined, message.timestamp);
this._addConsoleMessage(consoleMessage);
}
_consoleTimestampsSettingChanged() {
this._updateMessageList();
this._consoleMessages.forEach(viewMessage => viewMessage.updateTimestamp());
this._groupableMessageTitle.forEach(viewMessage => viewMessage.updateTimestamp());
}
_executionContextChanged() {
this._prompt.clearAutocomplete();
}
/**
* @override
*/
willHide() {
this._hidePromptSuggestBox();
}
/**
* @override
*/
wasShown() {
this._viewport.refresh();
}
/**
* @override
*/
focus() {
if (this._viewport.hasVirtualSelection()) {
this._viewport.contentElement().focus();
} else {
this._focusPrompt();
}
}
_focusPrompt() {
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;
}
}
/**
* @override
*/
restoreScrollPositions() {
if (this._viewport.stickToBottom()) {
this._immediatelyScrollToBottom();
} else {
super.restoreScrollPositions();
}
}
/**
* @override
*/
onResize() {
this._scheduleViewportRefresh();
this._hidePromptSuggestBox();
if (this._viewport.stickToBottom()) {
this._immediatelyScrollToBottom();
}
for (let i = 0; i < this._visibleViewMessages.length; ++i) {
this._visibleViewMessages[i].onResize();
}
}
_hidePromptSuggestBox() {
this._prompt.clearAutocomplete();
}
/**
* @return {!Promise.<undefined>}
*/
_invalidateViewport() {
if (this._muteViewportUpdates) {
this._maybeDirtyWhileMuted = true;
return Promise.resolve();
}
if (this._needsFullUpdate) {
this._updateMessageList();
delete this._needsFullUpdate;
} else {
this._viewport.invalidate();
}
return Promise.resolve();
}
_scheduleViewportRefresh() {
if (this._muteViewportUpdates) {
this._maybeDirtyWhileMuted = true;
this._scheduleViewportRefreshForTest(true);
return;
} else {
this._scheduleViewportRefreshForTest(false);
}
this._scheduledRefreshPromiseForTest = this._viewportThrottler.schedule(this._invalidateViewport.bind(this));
}
/**
* @param {boolean} muted
*/
_scheduleViewportRefreshForTest(muted) {
// This functions is sniffed in tests.
}
_immediatelyScrollToBottom() {
// This will scroll viewport and trigger its refresh.
this._viewport.setStickToBottom(true);
this._promptElement.scrollIntoView(true);
}
_updateFilterStatus() {
if (this._hiddenByFilterCount === this._lastShownHiddenByFilterCount) {
return;
}
this._filterStatusText.setText(ls`${this._hiddenByFilterCount} hidden`);
this._filterStatusText.setVisible(!!this._hiddenByFilterCount);
this._lastShownHiddenByFilterCount = this._hiddenByFilterCount;
}
/**
* @param {!Common.Event} event
*/
_onConsoleMessageAdded(event) {
const message = /** @type {!SDK.ConsoleMessage} */ (event.data);
this._addConsoleMessage(message);
}
/**
* @param {!SDK.ConsoleMessage} message
*/
_addConsoleMessage(message) {
const viewMessage = this._createViewMessage(message);
message[this._viewMessageSymbol] = viewMessage;
if (message.type === SDK.ConsoleMessage.MessageType.Command ||
message.type === SDK.ConsoleMessage.MessageType.Result) {
const lastMessage = this._consoleMessages.peekLast();
viewMessage[_messageSortingTimeSymbol] = lastMessage ? lastMessage[_messageSortingTimeSymbol] : 0;
} else {
viewMessage[_messageSortingTimeSymbol] = viewMessage.consoleMessage().timestamp;
}
let insertAt;
if (!this._consoleMessages.length ||
timeComparator(viewMessage, this._consoleMessages[this._consoleMessages.length - 1]) > 0) {
insertAt = this._consoleMessages.length;
} else {
insertAt = this._consoleMessages.upperBound(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;
if (message.isGroupable()) {
const groupKey = viewMessage.groupKey();
shouldGoIntoGroup = this._groupSimilarSetting.get() && 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);
this._updateFilterStatus();
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
} else {
this._needsFullUpdate = true;
}
this._scheduleViewportRefresh();
this._consoleMessageAddedForTest(viewMessage);
/**
* @param {!Console.ConsoleViewMessage} viewMessage1
* @param {!Console.ConsoleViewMessage} viewMessage2
*/
function timeComparator(viewMessage1, viewMessage2) {
return viewMessage1[_messageSortingTimeSymbol] - viewMessage2[_messageSortingTimeSymbol];
}
}
/**
* @param {!Common.Event} event
*/
_onConsoleMessageUpdated(event) {
const message = /** @type {!SDK.ConsoleMessage} */ (event.data);
const viewMessage = message[this._viewMessageSymbol];
if (viewMessage) {
viewMessage.updateMessageElement();
this._computeShouldMessageBeVisible(viewMessage);
this._updateMessageList();
}
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
*/
_consoleMessageAddedForTest(viewMessage) {
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
* @return {boolean}
*/
_shouldMessageBeVisible(viewMessage) {
return !this._shouldBeHiddenCache.has(viewMessage);
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
*/
_computeShouldMessageBeVisible(viewMessage) {
if (this._filter.shouldBeVisible(viewMessage) &&
(!this._isSidebarOpen || this._sidebar.shouldBeVisible(viewMessage))) {
this._shouldBeHiddenCache.delete(viewMessage);
} else {
this._shouldBeHiddenCache.add(viewMessage);
}
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
* @param {boolean=} preventCollapse
*/
_appendMessageToEnd(viewMessage, preventCollapse) {
if (!this._shouldMessageBeVisible(viewMessage)) {
this._hiddenByFilterCount++;
return;
}
if (!preventCollapse && this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast())) {
return;
}
const lastMessage = this._visibleViewMessages.peekLast();
if (viewMessage.consoleMessage().type === SDK.ConsoleMessage.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);
}
this._messageAppendedForTests();
}
_messageAppendedForTests() {
// This method is sniffed in tests.
}
/**
* @param {!SDK.ConsoleMessage} message
* @return {!Console.ConsoleViewMessage}
*/
_createViewMessage(message) {
const nestingLevel = this._currentGroup.nestingLevel();
switch (message.type) {
case SDK.ConsoleMessage.MessageType.Command:
return new ConsoleCommand(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
case SDK.ConsoleMessage.MessageType.Result:
return new ConsoleCommandResult(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
case SDK.ConsoleMessage.MessageType.StartGroupCollapsed:
case SDK.ConsoleMessage.MessageType.StartGroup:
return new Console.ConsoleGroupViewMessage(
message, this._linkifier, nestingLevel, this._updateMessageList.bind(this), this._onMessageResizedBound);
default:
return new Console.ConsoleViewMessage(message, this._linkifier, nestingLevel, this._onMessageResizedBound);
}
}
/**
* @param {!Common.Event} event
* @return {!Promise}
*/
async _onMessageResized(event) {
const treeElement = /** @type {!UI.TreeElement} */ (event.data);
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() {
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();
}
}
_handleContextMenuEvent(event) {
const contextMenu = new UI.ContextMenu(event);
if (event.target.isSelfOrDescendant(this._promptElement)) {
contextMenu.show();
return;
}
const sourceElement = event.target.enclosingNodeOrSelfWithClass('console-message-wrapper');
const consoleMessage = sourceElement ? sourceElement.message.consoleMessage() : null;
if (consoleMessage && consoleMessage.url) {
const menuTitle = ls`Hide messages from ${new Common.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(Common.UIString('Save as...'), this._saveConsole.bind(this));
if (this.element.hasSelection()) {
contextMenu.clipboardSection().appendItem(
Common.UIString('Copy visible styled selection'), this._viewport.copyWithStyles.bind(this._viewport));
}
if (consoleMessage) {
const request = SDK.NetworkLog.requestForConsoleMessage(consoleMessage);
if (request && SDK.NetworkManager.canReplayRequest(request)) {
contextMenu.debugSection().appendItem(ls`Replay XHR`, SDK.NetworkManager.replayRequest.bind(null, request));
}
}
contextMenu.show();
}
async _saveConsole() {
const url = SDK.targetManager.mainTarget().inspectedURL();
const parsedURL = Common.ParsedURL.fromString(url);
const filename = String.sprintf('%s-%d.log', parsedURL ? parsedURL.host : 'console', Date.now());
const stream = new Bindings.FileOutputStream();
const progressIndicator = new UI.ProgressIndicator();
progressIndicator.setTitle(Common.UIString('Writing file…'));
progressIndicator.setTotalWork(this.itemCount());
/** @const */
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 = /** @type {!Console.ConsoleViewMessage} */ (this.itemElement(messageIndex + i));
messageContents.push(message.toExportString());
}
messageIndex += i;
await stream.write(messageContents.join('\n') + '\n');
progressIndicator.setWorked(messageIndex);
}
stream.close();
progressIndicator.done();
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
* @param {!Console.ConsoleViewMessage=} lastMessage
* @return {boolean}
*/
_tryToCollapseMessages(viewMessage, lastMessage) {
const timestampsShown = this._timestampsSetting.get();
if (!timestampsShown && lastMessage && !viewMessage.consoleMessage().isGroupMessage() &&
viewMessage.consoleMessage().type !== SDK.ConsoleMessage.MessageType.Command &&
viewMessage.consoleMessage().type !== SDK.ConsoleMessage.MessageType.Result &&
viewMessage.consoleMessage().isEqual(lastMessage.consoleMessage())) {
lastMessage.incrementRepeatCount();
if (viewMessage.isLastInSimilarGroup()) {
lastMessage.setInSimilarGroup(true, true);
}
return true;
}
return false;
}
/**
* @param {number} startIndex
* @param {!Array<!Console.ConsoleViewMessage>} viewMessages
*/
_buildHiddenCache(startIndex, viewMessages) {
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() {
this._shouldBeHiddenCache.clear();
if (this._buildHiddenCacheTimeout) {
this.element.window().cancelAnimationFrame(this._buildHiddenCacheTimeout);
delete this._buildHiddenCacheTimeout;
}
}
_updateMessageList() {
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]);
}
}
this._updateFilterStatus();
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
this._viewport.invalidate();
}
_addGroupableMessagesToEnd() {
/** @type {!Set<!SDK.ConsoleMessage>} */
const alreadyAdded = new Set();
/** @type {!Set<string>} */
const processedGroupKeys = new Set();
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.
alreadyAdded.addAll(viewMessagesInGroup);
processedGroupKeys.add(key);
continue;
}
// Create artificial group start and end messages.
let startGroupViewMessage = this._groupableMessageTitle.get(key);
if (!startGroupViewMessage) {
const startGroupMessage = new SDK.ConsoleMessage(
null, message.source, message.level, viewMessage.groupTitle(),
SDK.ConsoleMessage.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.peekLast() === viewMessageInGroup);
this._appendMessageToEnd(viewMessageInGroup, true);
alreadyAdded.add(viewMessageInGroup.consoleMessage());
}
const endGroupMessage = new SDK.ConsoleMessage(
null, message.source, message.level, message.messageText, SDK.ConsoleMessage.MessageType.EndGroup);
this._appendMessageToEnd(this._createViewMessage(endGroupMessage));
}
}
/**
* @param {!Event} event
*/
_messagesClicked(event) {
const target = /** @type {?Node} */ (event.target);
// 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._focusPrompt();
}
}
}
/**
* @param {!Event} event
*/
_messagesKeyDown(event) {
const hasActionModifier = event.ctrlKey || event.altKey || event.metaKey;
if (hasActionModifier || event.key.length !== 1 || UI.isEditing() || this._messagesElement.hasSelection()) {
return;
}
this._prompt.moveCaretToEndOfPrompt();
this._focusPrompt();
}
/**
* @param {!Event} event
*/
_messagesPasted(event) {
if (UI.isEditing()) {
return;
}
this._prompt.focus();
}
_registerShortcuts() {
this._shortcuts = {};
this._shortcuts[UI.KeyboardShortcut.makeKey('u', UI.KeyboardShortcut.Modifiers.Ctrl)] =
this._clearPromptBackwards.bind(this);
}
_clearPromptBackwards() {
this._prompt.setText('');
}
/**
* @param {!Event} event
*/
_promptKeyDown(event) {
const keyboardEvent = /** @type {!KeyboardEvent} */ (event);
if (keyboardEvent.key === 'PageUp') {
this._updateStickToBottomOnWheel();
return;
}
const shortcut = UI.KeyboardShortcut.makeKeyFromEvent(keyboardEvent);
const handler = this._shortcuts[shortcut];
if (handler) {
handler();
keyboardEvent.preventDefault();
}
}
/**
* @param {?SDK.RemoteObject} result
* @param {!SDK.ConsoleMessage} originatingConsoleMessage
* @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails
*/
_printResult(result, originatingConsoleMessage, exceptionDetails) {
if (!result) {
return;
}
const level = !!exceptionDetails ? SDK.ConsoleMessage.MessageLevel.Error : SDK.ConsoleMessage.MessageLevel.Info;
let message;
if (!exceptionDetails) {
message = new SDK.ConsoleMessage(
result.runtimeModel(), SDK.ConsoleMessage.MessageSource.JS, level, '', SDK.ConsoleMessage.MessageType.Result,
undefined, undefined, undefined, [result]);
} else {
message = SDK.ConsoleMessage.fromException(
result.runtimeModel(), exceptionDetails, SDK.ConsoleMessage.MessageType.Result, undefined, undefined);
}
message.setOriginatingMessage(originatingConsoleMessage);
SDK.consoleModel.addMessage(message);
}
/**
* @param {!Common.Event} event
*/
_commandEvaluated(event) {
const data =
/** @type {{result: ?SDK.RemoteObject, commandMessage: !SDK.ConsoleMessage, exceptionDetails: (!Protocol.Runtime.ExceptionDetails|undefined)}} */
(event.data);
this._prompt.history().pushHistoryItem(data.commandMessage.messageText);
this._consoleHistorySetting.set(this._prompt.history().historyData().slice(-persistedHistorySize));
this._printResult(data.result, data.commandMessage, data.exceptionDetails);
}
/**
* @override
* @return {!Array.<!Element>}
*/
elementsToRestoreScrollPositionsFor() {
return [this._messagesElement];
}
/**
* @override
*/
searchCanceled() {
this._cleanupAfterSearch();
for (let i = 0; i < this._visibleViewMessages.length; ++i) {
const message = this._visibleViewMessages[i];
message.setSearchRegex(null);
}
this._currentMatchRangeIndex = -1;
this._regexMatchRanges = [];
delete this._searchRegex;
this._viewport.refresh();
}
/**
* @override
* @param {!UI.SearchableView.SearchConfig} searchConfig
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
performSearch(searchConfig, shouldJump, jumpBackwards) {
this.searchCanceled();
this._searchableView.updateSearchMatchesCount(0);
this._searchRegex = searchConfig.toSearchRegex(true);
this._regexMatchRanges = [];
this._currentMatchRangeIndex = -1;
if (shouldJump) {
this._searchShouldJumpBackwards = !!jumpBackwards;
}
this._searchProgressIndicator = new UI.ProgressIndicator();
this._searchProgressIndicator.setTitle(Common.UIString('Searching…'));
this._searchProgressIndicator.setTotalWork(this._visibleViewMessages.length);
this._progressToolbarItem.element.appendChild(this._searchProgressIndicator.element);
this._innerSearch(0);
}
_cleanupAfterSearch() {
delete this._searchShouldJumpBackwards;
if (this._innerSearchTimeoutId) {
clearTimeout(this._innerSearchTimeoutId);
delete this._innerSearchTimeoutId;
}
if (this._searchProgressIndicator) {
this._searchProgressIndicator.done();
delete this._searchProgressIndicator;
}
}
_searchFinishedForTests() {
// This method is sniffed in tests.
}
/**
* @param {number} index
*/
_innerSearch(index) {
delete this._innerSearchTimeoutId;
if (this._searchProgressIndicator.isCanceled()) {
this._cleanupAfterSearch();
return;
}
const startTime = Date.now();
for (; index < this._visibleViewMessages.length && Date.now() - startTime < 100; ++index) {
this._searchMessage(index);
}
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
if (typeof this._searchShouldJumpBackwards !== 'undefined' && this._regexMatchRanges.length) {
this._jumpToMatch(this._searchShouldJumpBackwards ? -1 : 0);
delete this._searchShouldJumpBackwards;
}
if (index === this._visibleViewMessages.length) {
this._cleanupAfterSearch();
setTimeout(this._searchFinishedForTests.bind(this), 0);
return;
}
this._innerSearchTimeoutId = setTimeout(this._innerSearch.bind(this, index), 100);
this._searchProgressIndicator.setWorked(index);
}
/**
* @param {number} index
*/
_searchMessage(index) {
const message = this._visibleViewMessages[index];
message.setSearchRegex(this._searchRegex);
for (let i = 0; i < message.searchCount(); ++i) {
this._regexMatchRanges.push({messageIndex: index, matchIndex: i});
}
}
/**
* @override
*/
jumpToNextSearchResult() {
this._jumpToMatch(this._currentMatchRangeIndex + 1);
}
/**
* @override
*/
jumpToPreviousSearchResult() {
this._jumpToMatch(this._currentMatchRangeIndex - 1);
}
/**
* @override
* @return {boolean}
*/
supportsCaseSensitiveSearch() {
return true;
}
/**
* @override
* @return {boolean}
*/
supportsRegexSearch() {
return true;
}
/**
* @param {number} index
*/
_jumpToMatch(index) {
if (!this._regexMatchRanges.length) {
return;
}
let matchRange;
if (this._currentMatchRangeIndex >= 0) {
matchRange = this._regexMatchRanges[this._currentMatchRangeIndex];
const message = this._visibleViewMessages[matchRange.messageIndex];
message.searchHighlightNode(matchRange.matchIndex).classList.remove(UI.highlightedCurrentSearchResultClassName);
}
index = mod(index, this._regexMatchRanges.length);
this._currentMatchRangeIndex = index;
this._searchableView.updateCurrentMatchIndex(index);
matchRange = this._regexMatchRanges[index];
const message = this._visibleViewMessages[matchRange.messageIndex];
const highlightNode = message.searchHighlightNode(matchRange.matchIndex);
highlightNode.classList.add(UI.highlightedCurrentSearchResultClassName);
this._viewport.scrollItemIntoView(matchRange.messageIndex);
highlightNode.scrollIntoViewIfNeeded();
}
/**
* @param {boolean=} isRightClick
*/
_updateStickToBottomOnPointerDown(isRightClick) {
this._muteViewportUpdates = !isRightClick;
this._viewport.setStickToBottom(false);
if (this._waitForScrollTimeout) {
clearTimeout(this._waitForScrollTimeout);
delete this._waitForScrollTimeout;
}
}
_updateStickToBottomOnPointerUp() {
if (!this._muteViewportUpdates) {
return;
}
// Delay querying isScrolledToBottom to give time for smooth scroll
// events to arrive. The value for the longest timeout duration is
// retrieved from crbug.com/575409.
this._waitForScrollTimeout = setTimeout(updateViewportState.bind(this), 200);
/**
* @this {!Console.ConsoleView}
*/
function updateViewportState() {
this._muteViewportUpdates = false;
if (this.isShowing()) {
this._viewport.setStickToBottom(this._isScrolledToBottom());
}
if (this._maybeDirtyWhileMuted) {
this._scheduleViewportRefresh();
delete this._maybeDirtyWhileMuted;
}
delete this._waitForScrollTimeout;
this._updateViewportStickinessForTest();
}
}
_updateViewportStickinessForTest() {
// This method is sniffed in tests.
}
_updateStickToBottomOnWheel() {
this._updateStickToBottomOnPointerDown();
this._updateStickToBottomOnPointerUp();
}
_promptTextChanged() {
const oldStickToBottom = this._viewport.stickToBottom();
const willStickToBottom = this._isScrolledToBottom();
this._viewport.setStickToBottom(willStickToBottom);
if (willStickToBottom && !oldStickToBottom) {
this._scheduleViewportRefresh();
}
this._promptTextChangedForTest();
}
_promptTextChangedForTest() {
// This method is sniffed in tests.
}
/**
* @return {boolean}
*/
_isScrolledToBottom() {
const distanceToPromptEditorBottom = this._messagesElement.scrollHeight - this._messagesElement.scrollTop -
this._messagesElement.clientHeight - this._prompt.belowEditorElement().offsetHeight;
return distanceToPromptEditorBottom <= 2;
}
}
const persistedHistorySize = 300;
/**
* @unrestricted
*/
export class ConsoleViewFilter {
/**
* @param {function()} filterChangedCallback
*/
constructor(filterChangedCallback) {
this._filterChanged = filterChangedCallback;
this._messageLevelFiltersSetting = ConsoleViewFilter.levelFilterSetting();
this._hideNetworkMessagesSetting = Common.moduleSetting('hideNetworkMessages');
this._filterByExecutionContextSetting = Common.moduleSetting('selectedContextFilterEnabled');
this._messageLevelFiltersSetting.addChangeListener(this._onFilterChanged.bind(this));
this._hideNetworkMessagesSetting.addChangeListener(this._onFilterChanged.bind(this));
this._filterByExecutionContextSetting.addChangeListener(this._onFilterChanged.bind(this));
UI.context.addFlavorChangeListener(SDK.ExecutionContext, this._onFilterChanged, this);
const filterKeys = Object.values(Console.ConsoleFilter.FilterType);
this._suggestionBuilder = new UI.FilterSuggestionBuilder(filterKeys);
this._textFilterUI = new UI.ToolbarInput(
Common.UIString('Filter'), '', 0.2, 1, Common.UIString('e.g. /event\\d/ -cdn url:a.com'),
this._suggestionBuilder.completions.bind(this._suggestionBuilder));
this._textFilterSetting = Common.settings.createSetting('console.textFilter', '');
if (this._textFilterSetting.get()) {
this._textFilterUI.setValue(this._textFilterSetting.get());
}
this._textFilterUI.addEventListener(UI.ToolbarInput.Event.TextChanged, () => {
this._textFilterSetting.set(this._textFilterUI.value());
this._onFilterChanged();
});
this._filterParser = new TextUtils.FilterParser(filterKeys);
this._currentFilter = new Console.ConsoleFilter('', [], null, this._messageLevelFiltersSetting.get());
this._updateCurrentFilter();
this._levelLabels = {};
this._levelLabels[SDK.ConsoleMessage.MessageLevel.Verbose] = Common.UIString('Verbose');
this._levelLabels[SDK.ConsoleMessage.MessageLevel.Info] = Common.UIString('Info');
this._levelLabels[SDK.ConsoleMessage.MessageLevel.Warning] = Common.UIString('Warnings');
this._levelLabels[SDK.ConsoleMessage.MessageLevel.Error] = Common.UIString('Errors');
this._levelMenuButton = new UI.ToolbarButton(ls`Log levels`);
this._levelMenuButton.turnIntoSelect();
this._levelMenuButton.addEventListener(UI.ToolbarButton.Events.Click, this._showLevelContextMenu.bind(this));
UI.ARIAUtils.markAsMenuButton(this._levelMenuButton.element);
this._updateLevelMenuButtonText();
this._messageLevelFiltersSetting.addChangeListener(this._updateLevelMenuButtonText.bind(this));
}
/**
* @param {!SDK.ConsoleMessage} message
*/
onMessageAdded(message) {
if (message.type === SDK.ConsoleMessage.MessageType.Command ||
message.type === SDK.ConsoleMessage.MessageType.Result || message.isGroupMessage()) {
return;
}
if (message.context) {
this._suggestionBuilder.addItem(Console.ConsoleFilter.FilterType.Context, message.context);
}
if (message.source) {
this._suggestionBuilder.addItem(Console.ConsoleFilter.FilterType.Source, message.source);
}
if (message.url) {
this._suggestionBuilder.addItem(Console.ConsoleFilter.FilterType.Url, message.url);
}
}
/**
* @return {!Common.Setting}
*/
static levelFilterSetting() {
return Common.settings.createSetting('messageLevelFilters', Console.ConsoleFilter.defaultLevelsFilterValue());
}
_updateCurrentFilter() {
const parsedFilters = this._filterParser.parse(this._textFilterUI.value());
if (this._hideNetworkMessagesSetting.get()) {
parsedFilters.push({
key: Console.ConsoleFilter.FilterType.Source,
text: SDK.ConsoleMessage.MessageSource.Network,
negative: true
});
}
this._currentFilter.executionContext =
this._filterByExecutionContextSetting.get() ? UI.context.flavor(SDK.ExecutionContext) : null;
this._currentFilter.parsedFilters = parsedFilters;
this._currentFilter.levelsMask = this._messageLevelFiltersSetting.get();
}
_onFilterChanged() {
this._updateCurrentFilter();
this._filterChanged();
}
_updateLevelMenuButtonText() {
let isAll = true;
let isDefault = true;
const allValue = Console.ConsoleFilter.allLevelsFilterValue();
const defaultValue = Console.ConsoleFilter.defaultLevelsFilterValue();
let text = null;
const levels = this._messageLevelFiltersSetting.get();
for (const name of Object.values(SDK.ConsoleMessage.MessageLevel)) {
isAll = isAll && levels[name] === allValue[name];
isDefault = isDefault && levels[name] === defaultValue[name];
if (levels[name]) {
text = text ? Common.UIString('Custom levels') : Common.UIString('%s only', this._levelLabels[name]);
}
}
if (isAll) {
text = Common.UIString('All levels');
} else if (isDefault) {
text = Common.UIString('Default levels');
} else {
text = text || Common.UIString('Hide all');
}
this._levelMenuButton.element.classList.toggle('warning', !isAll && !isDefault);
this._levelMenuButton.setText(text);
this._levelMenuButton.setTitle(ls`Log level: ${text}`);
}
/**
* @param {!Common.Event} event
*/
_showLevelContextMenu(event) {
const mouseEvent = /** @type {!Event} */ (event.data);
const setting = this._messageLevelFiltersSetting;
const levels = setting.get();
const contextMenu = new UI.ContextMenu(
mouseEvent, true /* useSoftMenu */, this._levelMenuButton.element.totalOffsetLeft(),
this._levelMenuButton.element.totalOffsetTop() + this._levelMenuButton.element.offsetHeight);
contextMenu.headerSection().appendItem(
Common.UIString('Default'), () => setting.set(Console.ConsoleFilter.defaultLevelsFilterValue()));
for (const level in this._levelLabels) {
contextMenu.defaultSection().appendCheckboxItem(
this._levelLabels[level], toggleShowLevel.bind(null, level), levels[level]);
}
contextMenu.show();
/**
* @param {string} level
*/
function toggleShowLevel(level) {
levels[level] = !levels[level];
setting.set(levels);
}
}
/**
* @param {string} url
*/
addMessageURLFilter(url) {
if (!url) {
return;
}
const suffix = this._textFilterUI.value() ? ` ${this._textFilterUI.value()}` : '';
this._textFilterUI.setValue(`-url:${url}${suffix}`);
this._textFilterSetting.set(this._textFilterUI.value());
this._onFilterChanged();
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
* @return {boolean}
*/
shouldBeVisible(viewMessage) {
return this._currentFilter.shouldBeVisible(viewMessage);
}
clear() {
this._suggestionBuilder.clear();
}
reset() {
this._messageLevelFiltersSetting.set(Console.ConsoleFilter.defaultLevelsFilterValue());
this._filterByExecutionContextSetting.set(false);
this._hideNetworkMessagesSetting.set(false);
this._textFilterUI.setValue('');
this._onFilterChanged();
}
}
/**
* @unrestricted
*/
export class ConsoleCommand extends Console.ConsoleViewMessage {
/**
* @override
* @return {!Element}
*/
contentElement() {
if (!this._contentElement) {
this._contentElement = createElementWithClass('div', 'console-user-command');
const icon = UI.Icon.create('smallicon-user-command', 'command-result-icon');
this._contentElement.appendChild(icon);
this._contentElement.message = this;
this._formattedCommand = createElementWithClass('span', 'source-code');
this._formattedCommand.textContent = this.text.replaceControlCharacters();
this._contentElement.appendChild(this._formattedCommand);
if (this._formattedCommand.textContent.length < MaxLengthToIgnoreHighlighter) {
const javascriptSyntaxHighlighter = new UI.SyntaxHighlighter('text/javascript', true);
javascriptSyntaxHighlighter.syntaxHighlightNode(this._formattedCommand).then(this._updateSearch.bind(this));
} else {
this._updateSearch();
}
this.updateTimestamp();
}
return this._contentElement;
}
_updateSearch() {
this.setSearchRegex(this.searchRegex());
}
}
/**
* @unrestricted
*/
class ConsoleCommandResult extends Console.ConsoleViewMessage {
/**
* @override
* @return {!Element}
*/
contentElement() {
const element = super.contentElement();
if (!element.classList.contains('console-user-command-result')) {
element.classList.add('console-user-command-result');
if (this.consoleMessage().level === SDK.ConsoleMessage.MessageLevel.Info) {
const icon = UI.Icon.create('smallicon-command-result', 'command-result-icon');
element.insertBefore(icon, element.firstChild);
}
}
return element;
}
}
/**
* @unrestricted
*/
export class ConsoleGroup {
/**
* @param {?Console.ConsoleGroup} parentGroup
* @param {?Console.ConsoleViewMessage} groupMessage
*/
constructor(parentGroup, groupMessage) {
this._parentGroup = parentGroup;
this._nestingLevel = parentGroup ? parentGroup.nestingLevel() + 1 : 0;
this._messagesHidden =
groupMessage && groupMessage.collapsed() || this._parentGroup && this._parentGroup.messagesHidden();
}
/**
* @return {!Console.ConsoleGroup}
*/
static createTopGroup() {
return new ConsoleGroup(null, null);
}
/**
* @return {boolean}
*/
messagesHidden() {
return this._messagesHidden;
}
/**
* @return {number}
*/
nestingLevel() {
return this._nestingLevel;
}
/**
* @return {?Console.ConsoleGroup}
*/
parentGroup() {
return this._parentGroup;
}
}
/**
* @implements {UI.ActionDelegate}
*/
export class ActionDelegate {
/**
* @override
* @param {!UI.Context} context
* @param {string} actionId
* @return {boolean}
*/
handleAction(context, actionId) {
switch (actionId) {
case 'console.show':
Host.InspectorFrontendHost.bringToFront();
Common.console.show();
ConsoleView.instance()._focusPrompt();
return true;
case 'console.clear':
ConsoleView.clearConsole();
return true;
case 'console.clear.history':
ConsoleView.instance()._clearHistory();
return true;
case 'console.create-pin':
ConsoleView.instance()._pinPane.addPin('', true /* userGesture */);
return true;
}
return false;
}
}
/** @type {symbol} */
const _messageSortingTimeSymbol = Symbol('messageSortingTime');
/**
* The maximum length before strings are considered too long for syntax highlighting.
* @const
* @type {number}
*/
const MaxLengthToIgnoreHighlighter = 10000;
/* Legacy exported object */
self.Console = self.Console || {};
/* Legacy exported object */
Console = Console || {};
/**
* @constructor
*/
Console.ConsoleView = ConsoleView;
/** @constructor */
Console.ConsoleViewFilter = ConsoleViewFilter;
/**
* @typedef {{messageIndex: number, matchIndex: number}}
*/
Console.ConsoleView.RegexMatchRange;
/**
* @implements {UI.ActionDelegate}
*/
Console.ConsoleView.ActionDelegate = ActionDelegate;
/**
* @constructor
*/
Console.ConsoleCommand = ConsoleCommand;
/**
* @constructor
*/
Console.ConsoleGroup = ConsoleGroup;