blob: d0f9972f2329d9c763e4d166ba0bffe024eb029a [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 {SDK.TargetManager.Observer}
* @implements {UI.ViewportControl.Provider}
* @unrestricted
*/
Console.ConsoleView = class extends UI.VBox {
constructor() {
super();
this.setMinimumSize(0, 35);
this.registerRequiredCSS('console/consoleView.css');
this._searchableView = new UI.SearchableView(this);
this._searchableView.setPlaceholder(Common.UIString('Find string in logs'));
this._searchableView.setMinimalSearchQuerySize(0);
this._searchableView.show(this.element);
this._contentsElement = this._searchableView.element;
this._contentsElement.classList.add('console-view');
/** @type {!Array.<!Console.ConsoleViewMessage>} */
this._visibleViewMessages = [];
this._urlToMessageCount = {};
this._hiddenByFilterCount = 0;
/**
* @type {!Array.<!Console.ConsoleView.RegexMatchRange>}
*/
this._regexMatchRanges = [];
this._executionContextComboBox = new UI.ToolbarComboBox(null, 'console-context');
this._executionContextComboBox.setMaxWidth(200);
this._consoleContextSelector = new Console.ConsoleContextSelector(this._executionContextComboBox.selectElement());
this._filter = new Console.ConsoleViewFilter(this);
this._filter.addEventListener(Console.ConsoleViewFilter.Events.FilterChanged, this._updateMessageList.bind(this));
this._filterBar = new UI.FilterBar('consoleView');
this._preserveLogCheckbox = new UI.ToolbarCheckbox(
Common.UIString('Preserve log'), Common.UIString('Do not clear log on page reload / navigation'),
Common.moduleSetting('preserveConsoleLog'));
this._progressToolbarItem = new UI.ToolbarItem(createElement('div'));
var toolbar = new UI.Toolbar('', this._contentsElement);
toolbar.appendToolbarItem(UI.Toolbar.createActionButton(
/** @type {!UI.Action }*/ (UI.actionRegistry.action('console.clear'))));
toolbar.appendToolbarItem(this._filterBar.filterButton());
toolbar.appendToolbarItem(this._executionContextComboBox);
toolbar.appendToolbarItem(this._preserveLogCheckbox);
toolbar.appendToolbarItem(this._progressToolbarItem);
this._filterBar.show(this._contentsElement);
this._filter.addFilters(this._filterBar);
this._viewport = new UI.ViewportControl(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), true);
this._viewportThrottler = new Common.Throttler(50);
this._filterStatusMessageElement = createElementWithClass('div', 'console-message');
this._messagesElement.insertBefore(this._filterStatusMessageElement, this._messagesElement.firstChild);
this._filterStatusTextElement = this._filterStatusMessageElement.createChild('span', 'console-info');
this._filterStatusMessageElement.createTextChild(' ');
var resetFiltersLink = this._filterStatusMessageElement.createChild('span', 'console-info link');
resetFiltersLink.textContent = Common.UIString('Show all messages.');
resetFiltersLink.addEventListener('click', this._filter.reset.bind(this._filter), true);
this._topGroup = Console.ConsoleGroup.createTopGroup();
this._currentGroup = this._topGroup;
this._promptElement = this._messagesElement.createChild('div', 'source-code');
this._promptElement.id = 'console-prompt';
this._promptElement.addEventListener('input', this._promptInput.bind(this), false);
// FIXME: This is a workaround for the selection machinery bug. See crbug.com/410899
var selectAllFixer = this._messagesElement.createChild('div', 'console-view-fix-select-all');
selectAllFixer.textContent = '.';
this._showAllMessagesCheckbox = new UI.ToolbarCheckbox(Common.UIString('Show all messages'));
this._showAllMessagesCheckbox.inputElement.checked = true;
this._showAllMessagesCheckbox.inputElement.addEventListener('change', this._updateMessageList.bind(this), false);
this._showAllMessagesCheckbox.element.classList.add('hidden');
toolbar.appendToolbarItem(this._showAllMessagesCheckbox);
this._registerShortcuts();
this._messagesElement.addEventListener('contextmenu', this._handleContextMenuEvent.bind(this), false);
Common.moduleSetting('monitoringXHREnabled').addChangeListener(this._monitoringXHREnabledSettingChanged, this);
this._linkifier = new Components.Linkifier();
/** @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._consoleHistoryAutocompleteSetting = Common.moduleSetting('consoleHistoryAutocomplete');
this._consoleHistoryAutocompleteSetting.addChangeListener(this._consoleHistoryAutocompleteChanged, this);
var historyData = this._consoleHistorySetting.get();
this._prompt.history().setHistoryData(historyData);
this._consoleHistoryAutocompleteChanged();
this._updateFilterStatus();
Common.moduleSetting('consoleTimestampsEnabled').addChangeListener(this._consoleTimestampsSettingChanged, this);
this._registerWithMessageSink();
SDK.targetManager.observeTargets(this);
this._initConsoleMessages();
UI.context.addFlavorChangeListener(SDK.ExecutionContext, this._executionContextChanged, this);
this._messagesElement.addEventListener('mousedown', this._updateStickToBottomOnMouseDown.bind(this), false);
this._messagesElement.addEventListener('mouseup', this._updateStickToBottomOnMouseUp.bind(this), false);
this._messagesElement.addEventListener('mouseleave', this._updateStickToBottomOnMouseUp.bind(this), false);
this._messagesElement.addEventListener('wheel', this._updateStickToBottomOnWheel.bind(this), false);
}
/**
* @return {!Console.ConsoleView}
*/
static instance() {
if (!Console.ConsoleView._instance)
Console.ConsoleView._instance = new Console.ConsoleView();
return Console.ConsoleView._instance;
}
static clearConsole() {
for (var target of SDK.targetManager.targets()) {
target.runtimeModel.discardConsoleEntries();
target.consoleModel.requestClearMessages();
}
}
/**
* @return {!UI.SearchableView}
*/
searchableView() {
return this._searchableView;
}
_clearHistory() {
this._consoleHistorySetting.set([]);
this._prompt.history().setHistoryData([]);
}
_consoleHistoryAutocompleteChanged() {
this._prompt.setAddCompletionsFromHistory(this._consoleHistoryAutocompleteSetting.get());
}
_initConsoleMessages() {
var mainTarget = SDK.targetManager.mainTarget();
var resourceTreeModel = mainTarget && SDK.ResourceTreeModel.fromTarget(mainTarget);
var resourcesLoaded = !resourceTreeModel || resourceTreeModel.cachedResourcesLoaded();
if (!mainTarget || !resourcesLoaded) {
SDK.targetManager.addModelListener(
SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this._onResourceTreeModelLoaded,
this);
return;
}
this._fetchMultitargetMessages();
}
/**
* @param {!Common.Event} event
*/
_onResourceTreeModelLoaded(event) {
var resourceTreeModel = event.target;
if (resourceTreeModel.target() !== SDK.targetManager.mainTarget())
return;
SDK.targetManager.removeModelListener(
SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this._onResourceTreeModelLoaded,
this);
this._fetchMultitargetMessages();
}
_fetchMultitargetMessages() {
SDK.multitargetConsoleModel.addEventListener(SDK.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
SDK.multitargetConsoleModel.addEventListener(
SDK.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
SDK.multitargetConsoleModel.addEventListener(
SDK.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this);
SDK.multitargetConsoleModel.addEventListener(
SDK.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
SDK.multitargetConsoleModel.messages().forEach(this._addConsoleMessage, this);
this._viewport.invalidate();
}
/**
* @override
* @return {number}
*/
itemCount() {
return this._visibleViewMessages.length;
}
/**
* @override
* @param {number} index
* @return {?UI.ViewportElement}
*/
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;
}
/**
* @override
* @param {!SDK.Target} target
*/
targetAdded(target) {
this._viewport.invalidate();
this._updateAllMessagesCheckbox();
}
/**
* @override
* @param {!SDK.Target} target
*/
targetRemoved(target) {
this._updateAllMessagesCheckbox();
}
_updateAllMessagesCheckbox() {
var hasMultipleCotexts = SDK.targetManager.targets(SDK.Target.Capability.JS).length > 1;
this._showAllMessagesCheckbox.element.classList.toggle('hidden', !hasMultipleCotexts);
}
_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) {
var level = SDK.ConsoleMessage.MessageLevel.Debug;
switch (message.level) {
case Common.Console.MessageLevel.Error:
level = SDK.ConsoleMessage.MessageLevel.Error;
break;
case Common.Console.MessageLevel.Warning:
level = SDK.ConsoleMessage.MessageLevel.Warning;
break;
}
var consoleMessage = new SDK.ConsoleMessage(
null, SDK.ConsoleMessage.MessageSource.Other, level, message.text, undefined, undefined, undefined, undefined,
undefined, undefined, undefined, message.timestamp);
this._addConsoleMessage(consoleMessage);
}
/**
* @param {!Common.Event} event
*/
_consoleTimestampsSettingChanged(event) {
var enabled = /** @type {boolean} */ (event.data);
this._updateMessageList();
this._consoleMessages.forEach(function(viewMessage) {
viewMessage.updateTimestamp(enabled);
});
}
_executionContextChanged() {
this._prompt.clearAutocomplete();
if (!this._showAllMessagesCheckbox.checked())
this._updateMessageList();
}
/**
* @override
*/
willHide() {
this._hidePromptSuggestBox();
}
/**
* @override
*/
wasShown() {
this._viewport.refresh();
}
/**
* @override
*/
focus() {
if (this._prompt.hasFocus())
return;
// Set caret position before setting focus in order to avoid scrolling
// by focus().
this._prompt.moveCaretToEndOfPrompt();
this._prompt.focus();
}
/**
* @override
*/
restoreScrollPositions() {
if (this._viewport.stickToBottom())
this._immediatelyScrollToBottom();
else
super.restoreScrollPositions();
}
/**
* @override
*/
onResize() {
this._scheduleViewportRefresh();
this._hidePromptSuggestBox();
if (this._viewport.stickToBottom())
this._immediatelyScrollToBottom();
for (var i = 0; i < this._visibleViewMessages.length; ++i)
this._visibleViewMessages[i].onResize();
}
_hidePromptSuggestBox() {
this._prompt.clearAutocomplete();
}
_scheduleViewportRefresh() {
/**
* @this {Console.ConsoleView}
* @return {!Promise.<undefined>}
*/
function 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();
}
if (this._muteViewportUpdates) {
this._maybeDirtyWhileMuted = true;
this._scheduleViewportRefreshForTest(true);
return;
} else {
this._scheduleViewportRefreshForTest(false);
}
this._viewportThrottler.schedule(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() {
this._filterStatusTextElement.textContent = Common.UIString(
this._hiddenByFilterCount === 1 ? '%d message is hidden by filters.' : '%d messages are hidden by filters.',
this._hiddenByFilterCount);
this._filterStatusMessageElement.style.display = this._hiddenByFilterCount ? '' : 'none';
}
/**
* @param {!Common.Event} event
*/
_onConsoleMessageAdded(event) {
var message = /** @type {!SDK.ConsoleMessage} */ (event.data);
this._addConsoleMessage(message);
}
/**
* @param {!SDK.ConsoleMessage} message
*/
_addConsoleMessage(message) {
/**
* @param {!Console.ConsoleViewMessage} viewMessage1
* @param {!Console.ConsoleViewMessage} viewMessage2
* @return {number}
*/
function compareTimestamps(viewMessage1, viewMessage2) {
return SDK.ConsoleMessage.timestampComparator(viewMessage1.consoleMessage(), viewMessage2.consoleMessage());
}
if (message.type === SDK.ConsoleMessage.MessageType.Command ||
message.type === SDK.ConsoleMessage.MessageType.Result) {
message.timestamp =
this._consoleMessages.length ? this._consoleMessages.peekLast().consoleMessage().timestamp : 0;
}
var viewMessage = this._createViewMessage(message);
message[this._viewMessageSymbol] = viewMessage;
var insertAt = this._consoleMessages.upperBound(viewMessage, compareTimestamps);
var insertedInMiddle = insertAt < this._consoleMessages.length;
this._consoleMessages.splice(insertAt, 0, viewMessage);
if (this._urlToMessageCount[message.url])
++this._urlToMessageCount[message.url];
else
this._urlToMessageCount[message.url] = 1;
if (!insertedInMiddle) {
this._appendMessageToEnd(viewMessage);
this._updateFilterStatus();
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
} else {
this._needsFullUpdate = true;
}
this._scheduleViewportRefresh();
this._consoleMessageAddedForTest(viewMessage);
}
/**
* @param {!Common.Event} event
*/
_onConsoleMessageUpdated(event) {
var message = /** @type {!SDK.ConsoleMessage} */ (event.data);
var viewMessage = message[this._viewMessageSymbol];
if (viewMessage) {
viewMessage.updateMessageElement();
this._updateMessageList();
}
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
*/
_consoleMessageAddedForTest(viewMessage) {
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
*/
_appendMessageToEnd(viewMessage) {
if (!this._filter.shouldBeVisible(viewMessage)) {
if (this._filter.shouldBeVisibleByDefault(viewMessage))
this._hiddenByFilterCount++;
return;
}
if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
return;
var lastMessage = this._visibleViewMessages.peekLast();
if (viewMessage.consoleMessage().type === SDK.ConsoleMessage.MessageType.EndGroup) {
if (lastMessage && !this._currentGroup.messagesHidden())
lastMessage.incrementCloseGroupDecorationCount();
this._currentGroup = this._currentGroup.parentGroup();
return;
}
if (!this._currentGroup.messagesHidden()) {
var originatingMessage = viewMessage.consoleMessage().originatingMessage();
if (lastMessage && originatingMessage && lastMessage.consoleMessage() === originatingMessage)
lastMessage.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 Console.ConsoleGroup(this._currentGroup, viewMessage);
this._messageAppendedForTests();
}
_messageAppendedForTests() {
// This method is sniffed in tests.
}
/**
* @param {!SDK.ConsoleMessage} message
* @return {!Console.ConsoleViewMessage}
*/
_createViewMessage(message) {
var nestingLevel = this._currentGroup.nestingLevel();
switch (message.type) {
case SDK.ConsoleMessage.MessageType.Command:
return new Console.ConsoleCommand(message, this._linkifier, nestingLevel);
case SDK.ConsoleMessage.MessageType.Result:
return new Console.ConsoleCommandResult(message, this._linkifier, nestingLevel);
case SDK.ConsoleMessage.MessageType.StartGroupCollapsed:
case SDK.ConsoleMessage.MessageType.StartGroup:
return new Console.ConsoleGroupViewMessage(message, this._linkifier, nestingLevel);
default:
return new Console.ConsoleViewMessage(message, this._linkifier, nestingLevel);
}
}
_consoleCleared() {
this._currentMatchRangeIndex = -1;
this._consoleMessages = [];
this._updateMessageList();
this._hidePromptSuggestBox();
this._viewport.setStickToBottom(true);
this._linkifier.reset();
}
_handleContextMenuEvent(event) {
if (event.target.enclosingNodeOrSelfWithNodeName('a'))
return;
var contextMenu = new UI.ContextMenu(event);
if (event.target.isSelfOrDescendant(this._promptElement)) {
contextMenu.show();
return;
}
function monitoringXHRItemAction() {
Common.moduleSetting('monitoringXHREnabled').set(!Common.moduleSetting('monitoringXHREnabled').get());
}
contextMenu.appendCheckboxItem(
Common.UIString('Log XMLHttpRequests'), monitoringXHRItemAction,
Common.moduleSetting('monitoringXHREnabled').get());
var sourceElement = event.target.enclosingNodeOrSelfWithClass('console-message-wrapper');
var consoleMessage = sourceElement ? sourceElement.message.consoleMessage() : null;
var filterSubMenu = contextMenu.appendSubMenuItem(Common.UIString('Filter'));
if (consoleMessage && consoleMessage.url) {
var menuTitle =
Common.UIString.capitalize('Hide ^messages from %s', new Common.ParsedURL(consoleMessage.url).displayName);
filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
}
filterSubMenu.appendSeparator();
var unhideAll = filterSubMenu.appendItem(
Common.UIString.capitalize('Unhide ^all'), this._filter.removeMessageURLFilter.bind(this._filter));
filterSubMenu.appendSeparator();
var hasFilters = false;
for (var url in this._filter.messageURLFilters) {
filterSubMenu.appendCheckboxItem(
String.sprintf('%s (%d)', new Common.ParsedURL(url).displayName, this._urlToMessageCount[url]),
this._filter.removeMessageURLFilter.bind(this._filter, url), true);
hasFilters = true;
}
filterSubMenu.setEnabled(hasFilters || (consoleMessage && consoleMessage.url));
unhideAll.setEnabled(hasFilters);
contextMenu.appendSeparator();
contextMenu.appendAction('console.clear');
contextMenu.appendAction('console.clear.history');
contextMenu.appendItem(Common.UIString('Save as...'), this._saveConsole.bind(this));
var request = consoleMessage ? consoleMessage.request : null;
if (request && request.resourceType() === Common.resourceTypes.XHR) {
contextMenu.appendSeparator();
contextMenu.appendItem(Common.UIString('Replay XHR'), request.replayXHR.bind(request));
}
contextMenu.show();
}
_saveConsole() {
var url = SDK.targetManager.mainTarget().inspectedURL();
var parsedURL = url.asParsedURL();
var filename = String.sprintf('%s-%d.log', parsedURL ? parsedURL.host : 'console', Date.now());
var stream = new Bindings.FileOutputStream();
var progressIndicator = new UI.ProgressIndicator();
progressIndicator.setTitle(Common.UIString('Writing file…'));
progressIndicator.setTotalWork(this.itemCount());
/** @const */
var chunkSize = 350;
var messageIndex = 0;
stream.open(filename, openCallback.bind(this));
/**
* @param {boolean} accepted
* @this {Console.ConsoleView}
*/
function openCallback(accepted) {
if (!accepted)
return;
this._progressToolbarItem.element.appendChild(progressIndicator.element);
writeNextChunk.call(this, stream);
}
/**
* @param {!Common.OutputStream} stream
* @param {string=} error
* @this {Console.ConsoleView}
*/
function writeNextChunk(stream, error) {
if (messageIndex >= this.itemCount() || error) {
stream.close();
progressIndicator.done();
return;
}
var lines = [];
for (var i = 0; i < chunkSize && i + messageIndex < this.itemCount(); ++i) {
var message = this.itemElement(messageIndex + i);
var messageContent = message.contentElement().deepTextContent();
for (var j = 0; j < message.repeatCount(); ++j)
lines.push(messageContent);
}
messageIndex += i;
stream.write(lines.join('\n') + '\n', writeNextChunk.bind(this));
progressIndicator.setWorked(messageIndex);
}
}
/**
* @param {!Console.ConsoleViewMessage} lastMessage
* @param {?Console.ConsoleViewMessage=} viewMessage
* @return {boolean}
*/
_tryToCollapseMessages(lastMessage, viewMessage) {
if (!Common.moduleSetting('consoleTimestampsEnabled').get() && viewMessage &&
!lastMessage.consoleMessage().isGroupMessage() &&
lastMessage.consoleMessage().isEqual(viewMessage.consoleMessage())) {
viewMessage.incrementRepeatCount();
return true;
}
return false;
}
_updateMessageList() {
this._topGroup = Console.ConsoleGroup.createTopGroup();
this._currentGroup = this._topGroup;
this._regexMatchRanges = [];
this._hiddenByFilterCount = 0;
for (var i = 0; i < this._visibleViewMessages.length; ++i) {
this._visibleViewMessages[i].resetCloseGroupDecorationCount();
this._visibleViewMessages[i].resetIncrementRepeatCount();
}
this._visibleViewMessages = [];
for (var i = 0; i < this._consoleMessages.length; ++i)
this._appendMessageToEnd(this._consoleMessages[i]);
this._updateFilterStatus();
this._searchableView.updateSearchMatchesCount(this._regexMatchRanges.length);
this._viewport.invalidate();
}
/**
* @param {!Common.Event} event
*/
_monitoringXHREnabledSettingChanged(event) {
var enabled = /** @type {boolean} */ (event.data);
SDK.targetManager.targets().forEach(function(target) {
target.networkAgent().setMonitoringXHREnabled(enabled);
});
}
/**
* @param {!Event} event
*/
_messagesClicked(event) {
var targetElement = event.deepElementFromPoint();
if (!targetElement || targetElement.isComponentSelectionCollapsed())
this.focus();
var groupMessage = event.target.enclosingNodeOrSelfWithClass('console-group-title');
if (!groupMessage)
return;
var consoleGroupViewMessage = groupMessage.parentElement.message;
consoleGroupViewMessage.setCollapsed(!consoleGroupViewMessage.collapsed());
this._updateMessageList();
}
_registerShortcuts() {
this._shortcuts = {};
var shortcut = UI.KeyboardShortcut;
var section = Components.shortcutsScreen.section(Common.UIString('Console'));
var shortcutL = shortcut.makeDescriptor('l', UI.KeyboardShortcut.Modifiers.Ctrl);
var keys = [shortcutL];
if (Host.isMac()) {
var shortcutK = shortcut.makeDescriptor('k', UI.KeyboardShortcut.Modifiers.Meta);
keys.unshift(shortcutK);
}
section.addAlternateKeys(keys, Common.UIString('Clear console'));
keys = [shortcut.makeDescriptor(shortcut.Keys.Tab), shortcut.makeDescriptor(shortcut.Keys.Right)];
section.addRelatedKeys(keys, Common.UIString('Accept suggestion'));
var shortcutU = shortcut.makeDescriptor('u', UI.KeyboardShortcut.Modifiers.Ctrl);
this._shortcuts[shortcutU.key] = this._clearPromptBackwards.bind(this);
section.addAlternateKeys([shortcutU], Common.UIString('Clear console prompt'));
keys = [shortcut.makeDescriptor(shortcut.Keys.Down), shortcut.makeDescriptor(shortcut.Keys.Up)];
section.addRelatedKeys(keys, Common.UIString('Next/previous line'));
if (Host.isMac()) {
keys =
[shortcut.makeDescriptor('N', shortcut.Modifiers.Alt), shortcut.makeDescriptor('P', shortcut.Modifiers.Alt)];
section.addRelatedKeys(keys, Common.UIString('Next/previous command'));
}
section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), Common.UIString('Execute command'));
}
_clearPromptBackwards() {
this._prompt.setText('');
}
/**
* @param {!Event} event
*/
_promptKeyDown(event) {
var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
if (keyboardEvent.key === 'PageUp') {
this._updateStickToBottomOnWheel();
return;
}
var shortcut = UI.KeyboardShortcut.makeKeyFromEvent(keyboardEvent);
var 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;
var level = !!exceptionDetails ? SDK.ConsoleMessage.MessageLevel.Error : SDK.ConsoleMessage.MessageLevel.Log;
var message;
if (!exceptionDetails) {
message = new SDK.ConsoleMessage(
result.target(), SDK.ConsoleMessage.MessageSource.JS, level, '', SDK.ConsoleMessage.MessageType.Result,
undefined, undefined, undefined, undefined, [result]);
} else {
message = SDK.ConsoleMessage.fromException(
result.target(), exceptionDetails, SDK.ConsoleMessage.MessageType.Result, undefined, undefined);
}
message.setOriginatingMessage(originatingConsoleMessage);
result.target().consoleModel.addMessage(message);
}
/**
* @param {!Common.Event} event
*/
_commandEvaluated(event) {
var data =
/** @type {{result: ?SDK.RemoteObject, text: string, commandMessage: !SDK.ConsoleMessage, exceptionDetails: (!Protocol.Runtime.ExceptionDetails|undefined)}} */
(event.data);
this._prompt.history().pushHistoryItem(data.text);
this._consoleHistorySetting.set(
this._prompt.history().historyData().slice(-Console.ConsoleView.persistedHistorySize));
this._printResult(data.result, data.commandMessage, data.exceptionDetails);
}
/**
* @override
* @return {!Array.<!Element>}
*/
elementsToRestoreScrollPositionsFor() {
return [this._messagesElement];
}
/**
* @override
*/
searchCanceled() {
this._cleanupAfterSearch();
for (var i = 0; i < this._visibleViewMessages.length; ++i) {
var 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;
}
var 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) {
var message = this._visibleViewMessages[index];
message.setSearchRegex(this._searchRegex);
for (var 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;
var matchRange;
if (this._currentMatchRangeIndex >= 0) {
matchRange = this._regexMatchRanges[this._currentMatchRangeIndex];
var 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];
var message = this._visibleViewMessages[matchRange.messageIndex];
var highlightNode = message.searchHighlightNode(matchRange.matchIndex);
highlightNode.classList.add(UI.highlightedCurrentSearchResultClassName);
this._viewport.scrollItemIntoView(matchRange.messageIndex);
highlightNode.scrollIntoViewIfNeeded();
}
_updateStickToBottomOnMouseDown() {
this._muteViewportUpdates = true;
this._viewport.setStickToBottom(false);
if (this._waitForScrollTimeout) {
clearTimeout(this._waitForScrollTimeout);
delete this._waitForScrollTimeout;
}
}
_updateStickToBottomOnMouseUp() {
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;
this._viewport.setStickToBottom(this._messagesElement.isScrolledToBottom());
if (this._maybeDirtyWhileMuted) {
this._scheduleViewportRefresh();
delete this._maybeDirtyWhileMuted;
}
delete this._waitForScrollTimeout;
this._updateViewportStickinessForTest();
}
}
_updateViewportStickinessForTest() {
// This method is sniffed in tests.
}
_updateStickToBottomOnWheel() {
this._updateStickToBottomOnMouseDown();
this._updateStickToBottomOnMouseUp();
}
_promptInput(event) {
// Scroll to the bottom, except when the prompt is the only visible item.
if (this.itemCount() !== 0 && this._viewport.firstVisibleIndex() !== this.itemCount())
this._immediatelyScrollToBottom();
}
};
Console.ConsoleView.persistedHistorySize = 300;
/**
* @unrestricted
*/
Console.ConsoleViewFilter = class extends Common.Object {
/**
* @param {!Console.ConsoleView} view
*/
constructor(view) {
super();
this._messageURLFiltersSetting = Common.settings.createSetting('messageURLFilters', {});
this._messageLevelFiltersSetting = Common.settings.createSetting('messageLevelFilters', {});
this._view = view;
this._messageURLFilters = this._messageURLFiltersSetting.get();
this._filterChanged = this.dispatchEventToListeners.bind(this, Console.ConsoleViewFilter.Events.FilterChanged);
}
addFilters(filterBar) {
this._textFilterUI = new UI.TextFilterUI(true);
this._textFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
filterBar.addFilter(this._textFilterUI);
this._hideNetworkMessagesCheckbox =
new UI.CheckboxFilterUI('', Common.UIString('Hide network'), true, Common.moduleSetting('hideNetworkMessages'));
this._hideViolationMessagesCheckbox = new UI.CheckboxFilterUI(
'', Common.UIString('Hide violations'), false, Common.moduleSetting('hideViolationMessages'));
Common.moduleSetting('hideNetworkMessages').addChangeListener(this._filterChanged, this);
Common.moduleSetting('hideViolationMessages').addChangeListener(this._filterChanged, this);
filterBar.addFilter(this._hideNetworkMessagesCheckbox);
filterBar.addFilter(this._hideViolationMessagesCheckbox);
var levels = [
{name: SDK.ConsoleMessage.MessageLevel.Error, label: Common.UIString('Errors')},
{name: SDK.ConsoleMessage.MessageLevel.Warning, label: Common.UIString('Warnings')},
{name: SDK.ConsoleMessage.MessageLevel.Info, label: Common.UIString('Info')},
{name: SDK.ConsoleMessage.MessageLevel.Log, label: Common.UIString('Logs')},
{name: SDK.ConsoleMessage.MessageLevel.Debug, label: Common.UIString('Debug')},
{name: SDK.ConsoleMessage.MessageLevel.RevokedError, label: Common.UIString('Handled')}
];
this._levelFilterUI = new UI.NamedBitSetFilterUI(levels, this._messageLevelFiltersSetting);
this._levelFilterUI.addEventListener(UI.FilterUI.Events.FilterChanged, this._filterChanged, this);
filterBar.addFilter(this._levelFilterUI);
}
_textFilterChanged(event) {
this._filterRegex = this._textFilterUI.regex();
this._filterChanged();
}
/**
* @param {string} url
*/
addMessageURLFilter(url) {
this._messageURLFilters[url] = true;
this._messageURLFiltersSetting.set(this._messageURLFilters);
this._filterChanged();
}
/**
* @param {string} url
*/
removeMessageURLFilter(url) {
if (!url)
this._messageURLFilters = {};
else
delete this._messageURLFilters[url];
this._messageURLFiltersSetting.set(this._messageURLFilters);
this._filterChanged();
}
/**
* @returns {!Object}
*/
get messageURLFilters() {
return this._messageURLFilters;
}
/**
* @param {!Console.ConsoleViewMessage} viewMessage
* @return {boolean}
*/
shouldBeVisible(viewMessage) {
var message = viewMessage.consoleMessage();
var executionContext = UI.context.flavor(SDK.ExecutionContext);
if (!message.target())
return true;
if (!this._view._showAllMessagesCheckbox.checked() && executionContext) {
if (message.target() !== executionContext.target())
return false;
if (message.executionContextId && message.executionContextId !== executionContext.id)
return false;
}
if (Common.moduleSetting('hideNetworkMessages').get() &&
viewMessage.consoleMessage().source === SDK.ConsoleMessage.MessageSource.Network)
return false;
if (Common.moduleSetting('hideViolationMessages').get() &&
viewMessage.consoleMessage().source === SDK.ConsoleMessage.MessageSource.Violation)
return false;
if (viewMessage.consoleMessage().isGroupMessage())
return true;
if (message.type === SDK.ConsoleMessage.MessageType.Result ||
message.type === SDK.ConsoleMessage.MessageType.Command)
return true;
if (message.url && this._messageURLFilters[message.url])
return false;
if (message.level && !this._levelFilterUI.accept(message.level))
return false;
if (this._filterRegex) {
this._filterRegex.lastIndex = 0;
if (!viewMessage.matchesFilterRegex(this._filterRegex))
return false;
}
return true;
}
/**
* @return {boolean}
*/
shouldBeVisibleByDefault(viewMessage) {
return viewMessage.consoleMessage().source !== SDK.ConsoleMessage.MessageSource.Violation;
}
reset() {
this._messageURLFilters = {};
this._messageURLFiltersSetting.set(this._messageURLFilters);
this._messageLevelFiltersSetting.set({});
this._view._showAllMessagesCheckbox.inputElement.checked = true;
Common.moduleSetting('hideNetworkMessages').set(false);
Common.moduleSetting('hideViolationMessages').set(true);
this._textFilterUI.setValue('');
this._filterChanged();
}
};
/** @enum {symbol} */
Console.ConsoleViewFilter.Events = {
FilterChanged: Symbol('FilterChanged')
};
/**
* @unrestricted
*/
Console.ConsoleCommand = class extends Console.ConsoleViewMessage {
/**
* @param {!SDK.ConsoleMessage} message
* @param {!Components.Linkifier} linkifier
* @param {number} nestingLevel
*/
constructor(message, linkifier, nestingLevel) {
super(message, linkifier, nestingLevel);
}
/**
* @override
* @return {!Element}
*/
contentElement() {
if (!this._contentElement) {
this._contentElement = createElementWithClass('div', 'console-user-command');
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 < Console.ConsoleCommand.MaxLengthToIgnoreHighlighter) {
var javascriptSyntaxHighlighter = new UI.DOMSyntaxHighlighter('text/javascript', true);
javascriptSyntaxHighlighter.syntaxHighlightNode(this._formattedCommand).then(this._updateSearch.bind(this));
} else {
this._updateSearch();
}
}
return this._contentElement;
}
_updateSearch() {
this.setSearchRegex(this.searchRegex());
}
};
/**
* The maximum length before strings are considered too long for syntax highlighting.
* @const
* @type {number}
*/
Console.ConsoleCommand.MaxLengthToIgnoreHighlighter = 10000;
/**
* @unrestricted
*/
Console.ConsoleCommandResult = class extends Console.ConsoleViewMessage {
/**
* @param {!SDK.ConsoleMessage} message
* @param {!Components.Linkifier} linkifier
* @param {number} nestingLevel
*/
constructor(message, linkifier, nestingLevel) {
super(message, linkifier, nestingLevel);
}
/**
* @override
* @return {!Element}
*/
contentElement() {
var element = super.contentElement();
element.classList.add('console-user-command-result');
this.updateTimestamp(false);
return element;
}
};
/**
* @unrestricted
*/
Console.ConsoleGroup = class {
/**
* @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 Console.ConsoleGroup(null, null);
}
/**
* @return {boolean}
*/
messagesHidden() {
return this._messagesHidden;
}
/**
* @return {number}
*/
nestingLevel() {
return this._nestingLevel;
}
/**
* @return {?Console.ConsoleGroup}
*/
parentGroup() {
return this._parentGroup || this;
}
};
/**
* @implements {UI.ActionDelegate}
* @unrestricted
*/
Console.ConsoleView.ActionDelegate = class {
/**
* @override
* @param {!UI.Context} context
* @param {string} actionId
* @return {boolean}
*/
handleAction(context, actionId) {
switch (actionId) {
case 'console.show':
Common.console.show();
return true;
case 'console.clear':
Console.ConsoleView.clearConsole();
return true;
case 'console.clear.history':
Console.ConsoleView.instance()._clearHistory();
return true;
}
return false;
}
};
/**
* @typedef {{messageIndex: number, matchIndex: number}}
*/
Console.ConsoleView.RegexMatchRange;