2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * @extends {WebInspector.VBox}
33 * @implements {WebInspector.Searchable}
34 * @implements {WebInspector.TargetManager.Observer}
35 * @implements {WebInspector.ViewportControl.Provider}
37 WebInspector.ConsoleView = function()
39 WebInspector.VBox.call(this);
40 this.registerRequiredCSS("filter.css");
42 this._searchableView = new WebInspector.SearchableView(this);
43 this._searchableView.setMinimalSearchQuerySize(0);
44 this._searchableView.show(this.element);
46 this._contentsElement = this._searchableView.element;
47 this._contentsElement.classList.add("console-view");
48 this._visibleViewMessages = [];
49 this._urlToMessageCount = {};
50 this._hiddenByFilterCount = 0;
52 this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item");
53 this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this);
55 this._executionContextSelector = new WebInspector.StatusBarComboBox(this._executionContextChanged.bind(this), "console-context");
58 * @type {!Map.<!WebInspector.ExecutionContext, !Element>}
60 this._optionByExecutionContext = new Map();
62 this._filter = new WebInspector.ConsoleViewFilter(this);
63 this._filter.addEventListener(WebInspector.ConsoleViewFilter.Events.FilterChanged, this._updateMessageList.bind(this));
65 this._filterBar = new WebInspector.FilterBar();
67 this._preserveLogCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Preserve log"));
68 WebInspector.SettingsUI.bindCheckbox(this._preserveLogCheckbox.inputElement, WebInspector.settings.preserveConsoleLog);
69 this._preserveLogCheckbox.element.title = WebInspector.UIString("Do not clear log on page reload / navigation.");
71 var statusBarElement = this._contentsElement.createChild("div", "console-status-bar");
72 statusBarElement.appendChildren(this._clearConsoleButton.element, this._filterBar.filterButton().element, this._executionContextSelector.element, this._preserveLogCheckbox.element);
74 this._filtersContainer = this._contentsElement.createChild("div", "console-filters-header hidden");
75 this._filtersContainer.appendChild(this._filterBar.filtersElement());
76 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
77 this._filterBar.setName("consoleView");
78 this._filter.addFilters(this._filterBar);
80 this._viewport = new WebInspector.ViewportControl(this);
81 this._viewport.setStickToBottom(true);
82 this._viewport.contentElement().classList.add("console-group", "console-group-messages");
83 this._contentsElement.appendChild(this._viewport.element);
84 this._messagesElement = this._viewport.element;
85 this._messagesElement.id = "console-messages";
86 this._messagesElement.classList.add("monospace");
87 this._messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
89 this._viewportThrottler = new WebInspector.Throttler(50);
91 this._filterStatusMessageElement = document.createElementWithClass("div", "console-message");
92 this._messagesElement.insertBefore(this._filterStatusMessageElement, this._messagesElement.firstChild);
93 this._filterStatusTextElement = this._filterStatusMessageElement.createChild("span", "console-info");
94 this._filterStatusMessageElement.createTextChild(" ");
95 var resetFiltersLink = this._filterStatusMessageElement.createChild("span", "console-info node-link");
96 resetFiltersLink.textContent = WebInspector.UIString("Show all messages.");
97 resetFiltersLink.addEventListener("click", this._filter.reset.bind(this._filter), true);
99 this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
100 this._currentGroup = this._topGroup;
102 this._promptElement = this._messagesElement.createChild("div", "source-code");
103 this._promptElement.id = "console-prompt";
104 this._promptElement.spellcheck = false;
106 // FIXME: This is a workaround for the selection machinery bug. See crbug.com/410899
107 var selectAllFixer = this._messagesElement.createChild("div", "console-view-fix-select-all");
108 selectAllFixer.textContent = ".";
110 this._showAllMessagesCheckbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString("Show all messages"));
111 this._showAllMessagesCheckbox.inputElement.checked = true;
112 this._showAllMessagesCheckbox.inputElement.addEventListener("change", this._updateMessageList.bind(this), false);
114 this._showAllMessagesCheckbox.element.classList.add("hidden");
116 statusBarElement.appendChild(this._showAllMessagesCheckbox.element);
118 this._registerShortcuts();
120 this._messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
121 WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged, this);
123 this._linkifier = new WebInspector.Linkifier();
125 /** @type {!Array.<!WebInspector.ConsoleViewMessage>} */
126 this._consoleMessages = [];
128 this._prompt = new WebInspector.TextPromptWithHistory(WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext);
129 this._prompt.setSuggestBoxEnabled(true);
130 this._prompt.renderAsBlock();
131 this._prompt.attach(this._promptElement);
132 this._prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
133 this._prompt.setHistoryData(WebInspector.settings.consoleHistory.get());
134 var historyData = WebInspector.settings.consoleHistory.get();
135 this._prompt.setHistoryData(historyData);
137 this._updateFilterStatus();
138 WebInspector.settings.consoleTimestampsEnabled.addChangeListener(this._consoleTimestampsSettingChanged, this);
140 this._registerWithMessageSink();
141 WebInspector.targetManager.observeTargets(this);
142 WebInspector.targetManager.addModelListener(WebInspector.RuntimeModel, WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
143 WebInspector.targetManager.addModelListener(WebInspector.RuntimeModel, WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
144 this._initConsoleMessages();
146 WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executionContextChangedExternally, this);
149 WebInspector.ConsoleView.prototype = {
150 _initConsoleMessages: function()
152 var mainTarget = WebInspector.targetManager.mainTarget();
153 if (!WebInspector.isWorkerFrontend() && (!mainTarget || !mainTarget.resourceTreeModel.cachedResourcesLoaded())) {
154 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._onResourceTreeModelLoaded, this);
157 this._fetchMultitargetMessages();
161 * @param {!WebInspector.Event} event
163 _onResourceTreeModelLoaded: function(event)
165 var resourceTreeModel = event.target;
166 if (resourceTreeModel.target() !== WebInspector.targetManager.mainTarget())
168 WebInspector.targetManager.removeModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._onResourceTreeModelLoaded, this);
169 this._fetchMultitargetMessages();
172 _fetchMultitargetMessages: function()
174 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
175 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
176 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
177 WebInspector.multitargetConsoleModel.messages().forEach(this._addConsoleMessage, this);
183 itemCount: function()
185 return this._visibleViewMessages.length;
189 * @param {number} index
190 * @return {?WebInspector.ViewportElement}
192 itemElement: function(index)
194 return this._visibleViewMessages[index];
198 * @param {number} index
201 fastHeight: function(index)
203 return this._visibleViewMessages[index].fastHeight();
209 minimumRowHeight: function()
215 * @param {!WebInspector.Target} target
217 targetAdded: function(target)
219 this._viewport.invalidate();
220 target.runtimeModel.executionContexts().forEach(this._executionContextCreated, this);
221 if (WebInspector.targetManager.targets().length > 1)
222 this._showAllMessagesCheckbox.element.classList.toggle("hidden", false);
226 * @param {!WebInspector.Target} target
228 targetRemoved: function(target)
230 this._clearExecutionContextsForTarget(target);
233 _registerWithMessageSink: function()
235 WebInspector.console.messages().forEach(this._addSinkMessage, this);
236 WebInspector.console.addEventListener(WebInspector.Console.Events.MessageAdded, messageAdded, this);
239 * @param {!WebInspector.Event} event
240 * @this {WebInspector.ConsoleView}
242 function messageAdded(event)
244 this._addSinkMessage(/** @type {!WebInspector.Console.Message} */ (event.data));
249 * @param {!WebInspector.Console.Message} message
251 _addSinkMessage: function(message)
253 var level = WebInspector.ConsoleMessage.MessageLevel.Debug;
254 switch (message.level) {
255 case WebInspector.Console.MessageLevel.Error:
256 level = WebInspector.ConsoleMessage.MessageLevel.Error;
258 case WebInspector.Console.MessageLevel.Warning:
259 level = WebInspector.ConsoleMessage.MessageLevel.Warning;
263 var consoleMessage = new WebInspector.ConsoleMessage(null, WebInspector.ConsoleMessage.MessageSource.Other, level, message.text,
264 undefined, undefined, undefined, undefined, undefined, undefined, undefined, message.timestamp);
265 this._addConsoleMessage(consoleMessage);
269 * @param {!WebInspector.Event} event
271 _consoleTimestampsSettingChanged: function(event)
273 var enabled = /** @type {boolean} */ (event.data);
274 this._updateMessageList();
275 this._consoleMessages.forEach(function(viewMessage) {
276 viewMessage.updateTimestamp(enabled);
283 defaultFocusedElement: function()
285 return this._promptElement
288 _onFiltersToggled: function(event)
290 var toggled = /** @type {boolean} */ (event.data);
291 this._filtersContainer.classList.toggle("hidden", !toggled);
295 * @param {!WebInspector.ExecutionContext} executionContext
298 _titleFor: function(executionContext)
301 if (executionContext.isMainWorldContext) {
302 if (executionContext.frameId) {
303 var frame = executionContext.target().resourceTreeModel.frameForId(executionContext.frameId);
304 result = frame ? frame.displayName() : (executionContext.origin || executionContext.name);
306 result = WebInspector.displayNameForURL(executionContext.origin) || executionContext.name;
309 result = "\u00a0\u00a0\u00a0\u00a0" + (executionContext.name || executionContext.origin);
312 return result.trimMiddle(maxLength);
316 * @param {!WebInspector.Event} event
318 _onExecutionContextCreated: function(event)
320 var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
321 this._executionContextCreated(executionContext);
325 * @param {!WebInspector.ExecutionContext} executionContext
327 _executionContextCreated: function(executionContext)
329 var newOption = document.createElement("option");
330 newOption.__executionContext = executionContext;
331 newOption.text = this._titleFor(executionContext);
332 this._optionByExecutionContext.set(executionContext, newOption);
333 var sameGroupExists = false;
334 var options = this._executionContextSelector.selectElement().options;
335 var insertBeforeOption = null;
336 for (var i = 0; i < options.length; ++i) {
337 var optionContext = options[i].__executionContext;
338 var isSameGroup = executionContext.target() === optionContext.target() && executionContext.frameId === optionContext.frameId;
339 sameGroupExists |= isSameGroup;
340 if ((isSameGroup && WebInspector.ExecutionContext.comparator(optionContext, executionContext) > 0) || (sameGroupExists && !isSameGroup)) {
341 insertBeforeOption = options[i];
345 this._executionContextSelector.selectElement().insertBefore(newOption, insertBeforeOption);
346 if (executionContext === WebInspector.context.flavor(WebInspector.ExecutionContext))
347 this._executionContextSelector.select(newOption);
351 * @param {!WebInspector.Event} event
353 _onExecutionContextDestroyed: function(event)
355 var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
356 this._executionContextDestroyed(executionContext);
360 * @param {!WebInspector.ExecutionContext} executionContext
362 _executionContextDestroyed: function(executionContext)
364 var option = this._optionByExecutionContext.remove(executionContext);
369 * @param {!WebInspector.Target} target
371 _clearExecutionContextsForTarget: function(target)
373 var executionContexts = this._optionByExecutionContext.keys();
374 for (var i = 0; i < executionContexts.length; ++i) {
375 if (executionContexts[i].target() === target)
376 this._executionContextDestroyed(executionContexts[i]);
380 _executionContextChanged: function()
382 var newContext = this._currentExecutionContext();
383 WebInspector.context.setFlavor(WebInspector.ExecutionContext, newContext);
384 this._prompt.clearAutoComplete(true);
385 if (!this._showAllMessagesCheckbox.checked())
386 this._updateMessageList();
390 * @param {!WebInspector.Event} event
392 _executionContextChangedExternally: function(event)
394 var executionContext = /** @type {?WebInspector.ExecutionContext} */ (event.data);
395 if (!executionContext)
398 var options = this._executionContextSelector.selectElement().options;
399 for (var i = 0; i < options.length; ++i) {
400 if (options[i].__executionContext === executionContext)
401 this._executionContextSelector.select(options[i]);
406 * @return {?WebInspector.ExecutionContext}
408 _currentExecutionContext: function()
410 var option = this._executionContextSelector.selectedOption();
411 return option ? option.__executionContext : null;
416 this._prompt.hideSuggestBox();
417 this._prompt.clearAutoComplete(true);
422 this._viewport.refresh();
423 if (!this._prompt.isCaretInsidePrompt())
424 this._prompt.moveCaretToEndOfPrompt();
429 if (this._promptElement === WebInspector.currentFocusElement())
431 WebInspector.setCurrentFocusElement(this._promptElement);
432 this._prompt.moveCaretToEndOfPrompt();
435 restoreScrollPositions: function()
437 if (this._viewport.scrolledToBottom())
438 this._immediatelyScrollToBottom();
440 WebInspector.View.prototype.restoreScrollPositions.call(this);
445 this._scheduleViewportRefresh();
446 this._prompt.hideSuggestBox();
447 if (this._viewport.scrolledToBottom())
448 this._immediatelyScrollToBottom();
451 _scheduleViewportRefresh: function()
454 * @param {!WebInspector.Throttler.FinishCallback} finishCallback
455 * @this {WebInspector.ConsoleView}
457 function invalidateViewport(finishCallback)
459 this._viewport.invalidate();
462 this._viewportThrottler.schedule(invalidateViewport.bind(this));
465 _immediatelyScrollToBottom: function()
467 // This will scroll viewport and trigger its refresh.
468 this._promptElement.scrollIntoView(true);
471 _updateFilterStatus: function()
473 this._filterStatusTextElement.textContent = WebInspector.UIString(this._hiddenByFilterCount === 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", this._hiddenByFilterCount);
474 this._filterStatusMessageElement.style.display = this._hiddenByFilterCount ? "" : "none";
478 * @param {!WebInspector.ConsoleViewMessage} viewMessage
480 _consoleMessageAdded: function(viewMessage)
483 * @param {!WebInspector.ConsoleViewMessage} viewMessage1
484 * @param {!WebInspector.ConsoleViewMessage} viewMessage2
487 function compareTimestamps(viewMessage1, viewMessage2)
489 return WebInspector.ConsoleMessage.timestampComparator(viewMessage1.consoleMessage(), viewMessage2.consoleMessage());
491 var insertAt = insertionIndexForObjectInListSortedByFunction(viewMessage, this._consoleMessages, compareTimestamps, true);
492 this._consoleMessages.splice(insertAt, 0, viewMessage);
494 var message = viewMessage.consoleMessage();
495 if (this._urlToMessageCount[message.url])
496 this._urlToMessageCount[message.url]++;
498 this._urlToMessageCount[message.url] = 1;
500 if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
503 if (this._filter.shouldBeVisible(viewMessage))
504 this._showConsoleMessage(viewMessage)
506 this._hiddenByFilterCount++;
507 this._updateFilterStatus();
512 * @param {!WebInspector.Event} event
514 _onConsoleMessageAdded: function(event)
516 var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data);
517 this._addConsoleMessage(message);
521 * @param {!WebInspector.ConsoleMessage} message
523 _addConsoleMessage: function(message)
525 var viewMessage = this._createViewMessage(message);
526 this._consoleMessageAdded(viewMessage);
527 this._scheduleViewportRefresh();
531 * @param {!WebInspector.ConsoleViewMessage} viewMessage
533 _showConsoleMessage: function(viewMessage)
535 var lastMessage = this._visibleViewMessages.peekLast();
536 if (viewMessage.consoleMessage().type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
537 if (lastMessage && !this._currentGroup.messagesHidden())
538 lastMessage.incrementCloseGroupDecorationCount();
539 this._currentGroup = this._currentGroup.parentGroup();
542 if (!this._currentGroup.messagesHidden()) {
543 var originatingMessage = viewMessage.consoleMessage().originatingMessage();
544 if (lastMessage && originatingMessage && lastMessage.consoleMessage() === originatingMessage)
545 lastMessage.toMessageElement().classList.add("console-adjacent-user-command-result");
547 this._visibleViewMessages.push(viewMessage);
549 if (this._searchRegex && viewMessage.matchesRegex(this._searchRegex)) {
550 this._searchResults.push(viewMessage);
551 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
555 if (viewMessage.consoleMessage().isGroupStartMessage())
556 this._currentGroup = new WebInspector.ConsoleGroup(this._currentGroup, viewMessage);
560 * @param {!WebInspector.ConsoleMessage} message
561 * @return {!WebInspector.ConsoleViewMessage}
563 _createViewMessage: function(message)
565 var nestingLevel = this._currentGroup.nestingLevel();
566 switch (message.type) {
567 case WebInspector.ConsoleMessage.MessageType.Command:
568 return new WebInspector.ConsoleCommand(message, nestingLevel);
569 case WebInspector.ConsoleMessage.MessageType.Result:
570 return new WebInspector.ConsoleCommandResult(message, this._linkifier, nestingLevel);
571 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
572 case WebInspector.ConsoleMessage.MessageType.StartGroup:
573 return new WebInspector.ConsoleGroupViewMessage(message, this._linkifier, nestingLevel);
575 return new WebInspector.ConsoleViewMessage(message, this._linkifier, nestingLevel);
579 _consoleCleared: function()
581 this._clearCurrentSearchResultHighlight();
582 this._consoleMessages = [];
583 this._updateMessageList();
585 if (this._searchRegex)
586 this._searchableView.updateSearchMatchesCount(0);
588 this._linkifier.reset();
591 _handleContextMenuEvent: function(event)
593 if (event.target.enclosingNodeOrSelfWithNodeName("a"))
596 var contextMenu = new WebInspector.ContextMenu(event);
598 function monitoringXHRItemAction()
600 WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
602 contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction, WebInspector.settings.monitoringXHREnabled.get());
604 var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message-wrapper");
605 var consoleMessage = sourceElement ? sourceElement.message.consoleMessage() : null;
607 var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
609 if (consoleMessage && consoleMessage.url) {
610 var menuTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(consoleMessage.url).displayName);
611 filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
614 filterSubMenu.appendSeparator();
615 var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._filter.removeMessageURLFilter.bind(this._filter));
616 filterSubMenu.appendSeparator();
618 var hasFilters = false;
620 for (var url in this._filter.messageURLFilters) {
621 filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._filter.removeMessageURLFilter.bind(this._filter, url), true);
625 filterSubMenu.setEnabled(hasFilters || (consoleMessage && consoleMessage.url));
626 unhideAll.setEnabled(hasFilters);
628 contextMenu.appendSeparator();
629 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
631 var request = consoleMessage ? consoleMessage.request : null;
632 if (request && request.type === WebInspector.resourceTypes.XHR) {
633 contextMenu.appendSeparator();
634 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
641 * @param {!WebInspector.ConsoleViewMessage} lastMessage
642 * @param {?WebInspector.ConsoleViewMessage} viewMessage
645 _tryToCollapseMessages: function(lastMessage, viewMessage)
647 if (!WebInspector.settings.consoleTimestampsEnabled.get() && viewMessage && !lastMessage.consoleMessage().isGroupMessage() && lastMessage.consoleMessage().isEqual(viewMessage.consoleMessage())) {
648 viewMessage.incrementRepeatCount();
655 _updateMessageList: function()
657 this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
658 this._currentGroup = this._topGroup;
659 this._searchResults = [];
660 this._hiddenByFilterCount = 0;
661 for (var i = 0; i < this._visibleViewMessages.length; ++i) {
662 this._visibleViewMessages[i].resetCloseGroupDecorationCount();
663 this._visibleViewMessages[i].resetIncrementRepeatCount();
665 this._visibleViewMessages = [];
666 for (var i = 0; i < this._consoleMessages.length; ++i) {
667 var viewMessage = this._consoleMessages[i];
668 if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
670 if (this._filter.shouldBeVisible(viewMessage))
671 this._showConsoleMessage(viewMessage);
673 this._hiddenByFilterCount++;
675 this._updateFilterStatus();
676 this._viewport.invalidate();
680 * @param {!WebInspector.Event} event
682 _monitoringXHREnabledSettingChanged: function(event)
684 var enabled = /** @type {boolean} */ (event.data);
685 WebInspector.targetManager.targets().forEach(function(target) {target.consoleAgent().setMonitoringXHREnabled(enabled);});
689 * @param {!Event} event
691 _messagesClicked: function(event)
693 if (!this._prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
694 this._prompt.moveCaretToEndOfPrompt();
695 var groupMessage = event.target.enclosingNodeOrSelfWithClass("console-group-title");
698 var consoleGroupViewMessage = groupMessage.parentElement.message;
699 consoleGroupViewMessage.setCollapsed(!consoleGroupViewMessage.collapsed());
700 this._updateMessageList();
703 _registerShortcuts: function()
705 this._shortcuts = {};
707 var shortcut = WebInspector.KeyboardShortcut;
708 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
710 var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
711 this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
712 var keys = [shortcutL];
713 if (WebInspector.isMac()) {
714 var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
715 this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
716 keys.unshift(shortcutK);
718 section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
720 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
721 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
723 var shortcutU = shortcut.makeDescriptor("u", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
724 this._shortcuts[shortcutU.key] = this._clearPromptBackwards.bind(this);
725 section.addAlternateKeys([shortcutU], WebInspector.UIString("Clear console prompt"));
728 shortcut.makeDescriptor(shortcut.Keys.Down),
729 shortcut.makeDescriptor(shortcut.Keys.Up)
731 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
733 if (WebInspector.isMac()) {
735 shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
736 shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
738 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
741 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
744 _clearPromptBackwards: function()
746 this._prompt.text = "";
749 _requestClearMessages: function()
751 var targets = WebInspector.targetManager.targets();
752 for (var i = 0; i < targets.length; ++i)
753 targets[i].consoleModel.requestClearMessages();
756 _promptKeyDown: function(event)
758 if (isEnterKey(event)) {
759 this._enterKeyPressed(event);
763 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
764 var handler = this._shortcuts[shortcut];
767 event.preventDefault();
771 _enterKeyPressed: function(event)
773 if (event.altKey || event.ctrlKey || event.shiftKey)
778 this._prompt.clearAutoComplete(true);
780 var str = this._prompt.text;
783 this._appendCommand(str, true);
787 * @param {?WebInspector.RemoteObject} result
788 * @param {boolean} wasThrown
789 * @param {!WebInspector.ConsoleMessage} originatingConsoleMessage
790 * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
792 _printResult: function(result, wasThrown, originatingConsoleMessage, exceptionDetails)
797 var target = result.target();
799 * @param {string=} url
800 * @param {number=} lineNumber
801 * @param {number=} columnNumber
803 function addMessage(url, lineNumber, columnNumber)
805 var level = wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
808 message = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, url, lineNumber, columnNumber, undefined, [result]);
810 message = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, level, exceptionDetails.text, WebInspector.ConsoleMessage.MessageType.Result, exceptionDetails.url, exceptionDetails.line, exceptionDetails.column, undefined, [WebInspector.UIString("Uncaught"), result], exceptionDetails.stackTrace);
811 message.setOriginatingMessage(originatingConsoleMessage);
812 target.consoleModel.addMessage(message);
815 if (result.type !== "function") {
820 result.functionDetails(didGetDetails);
823 * @param {?WebInspector.DebuggerModel.FunctionDetails} response
825 function didGetDetails(response)
827 if (!response || !response.location) {
834 var script = target.debuggerModel.scriptForId(response.location.scriptId);
835 if (script && script.sourceURL) {
836 url = script.sourceURL;
837 // FIXME(WK62725): Debugger line/column are 0-based, while console ones are 1-based.
838 lineNumber = response.location.lineNumber + 1;
839 columnNumber = response.location.columnNumber + 1;
841 // FIXME: this should be using live location.
842 addMessage(url, lineNumber, columnNumber);
847 * @param {string} text
848 * @param {boolean} useCommandLineAPI
850 _appendCommand: function(text, useCommandLineAPI)
853 this._prompt.text = "";
854 var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
855 if (currentExecutionContext)
856 WebInspector.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, text, useCommandLineAPI);
860 * @param {!WebInspector.Event} event
862 _commandEvaluated: function(event)
864 var data = /**{{result: ?WebInspector.RemoteObject, wasThrown: boolean, text: string, commandMessage: !WebInspector.ConsoleMessage}} */ (event.data);
865 this._prompt.pushHistoryItem(data.text);
866 WebInspector.settings.consoleHistory.set(this._prompt.historyData.slice(-30));
867 this._printResult(data.result, data.wasThrown, data.commandMessage, data.exceptionDetails);
871 * @return {!Array.<!Element>}
873 elementsToRestoreScrollPositionsFor: function()
875 return [this._messagesElement];
878 searchCanceled: function()
880 this._clearCurrentSearchResultHighlight();
881 delete this._searchResults;
882 delete this._searchRegex;
883 this._viewport.refresh();
887 * @param {string} query
888 * @param {boolean} shouldJump
889 * @param {boolean=} jumpBackwards
891 performSearch: function(query, shouldJump, jumpBackwards)
893 this.searchCanceled();
894 this._searchableView.updateSearchMatchesCount(0);
895 this._searchRegex = createPlainTextSearchRegex(query, "gi");
897 /** @type {!Array.<number>} */
898 this._searchResults = [];
899 for (var i = 0; i < this._visibleViewMessages.length; i++) {
900 if (this._visibleViewMessages[i].matchesRegex(this._searchRegex))
901 this._searchResults.push(i);
903 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
904 this._currentSearchResultIndex = -1;
905 if (shouldJump && this._searchResults.length)
906 this._jumpToSearchResult(jumpBackwards ? -1 : 0);
907 this._viewport.refresh();
910 jumpToNextSearchResult: function()
912 if (!this._searchResults || !this._searchResults.length)
914 this._jumpToSearchResult(this._currentSearchResultIndex + 1);
917 jumpToPreviousSearchResult: function()
919 if (!this._searchResults || !this._searchResults.length)
921 this._jumpToSearchResult(this._currentSearchResultIndex - 1);
924 _clearCurrentSearchResultHighlight: function()
926 if (!this._searchResults)
929 var highlightedViewMessage = this._visibleViewMessages[this._searchResults[this._currentSearchResultIndex]];
930 if (highlightedViewMessage)
931 highlightedViewMessage.clearHighlight();
932 this._currentSearchResultIndex = -1;
935 _jumpToSearchResult: function(index)
937 index = mod(index, this._searchResults.length);
938 this._clearCurrentSearchResultHighlight();
939 this._currentSearchResultIndex = index;
940 this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex);
941 var currentViewMessageIndex = this._searchResults[index];
942 this._viewport.scrollItemIntoView(currentViewMessageIndex);
943 this._visibleViewMessages[currentViewMessageIndex].highlightSearchResults(this._searchRegex);
946 __proto__: WebInspector.VBox.prototype
951 * @extends {WebInspector.Object}
952 * @param {!WebInspector.ConsoleView} view
954 WebInspector.ConsoleViewFilter = function(view)
957 this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
958 this._filterChanged = this.dispatchEventToListeners.bind(this, WebInspector.ConsoleViewFilter.Events.FilterChanged);
961 WebInspector.ConsoleViewFilter.Events = {
962 FilterChanged: "FilterChanged"
965 WebInspector.ConsoleViewFilter.prototype = {
966 addFilters: function(filterBar)
968 this._textFilterUI = new WebInspector.TextFilterUI(true);
969 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
970 filterBar.addFilter(this._textFilterUI);
973 {name: "error", label: WebInspector.UIString("Errors")},
974 {name: "warning", label: WebInspector.UIString("Warnings")},
975 {name: "info", label: WebInspector.UIString("Info")},
976 {name: "log", label: WebInspector.UIString("Logs")},
977 {name: "debug", label: WebInspector.UIString("Debug")}
979 this._levelFilterUI = new WebInspector.NamedBitSetFilterUI(levels, WebInspector.settings.messageLevelFilters);
980 this._levelFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
981 filterBar.addFilter(this._levelFilterUI);
984 _textFilterChanged: function(event)
986 this._filterRegex = this._textFilterUI.regex();
988 this._filterChanged();
992 * @param {string} url
994 addMessageURLFilter: function(url)
996 this._messageURLFilters[url] = true;
997 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
998 this._filterChanged();
1002 * @param {string} url
1004 removeMessageURLFilter: function(url)
1007 this._messageURLFilters = {};
1009 delete this._messageURLFilters[url];
1011 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
1012 this._filterChanged();
1016 * @returns {!Object}
1018 get messageURLFilters()
1020 return this._messageURLFilters;
1024 * @param {!WebInspector.ConsoleViewMessage} viewMessage
1027 shouldBeVisible: function(viewMessage)
1029 var message = viewMessage.consoleMessage();
1030 var executionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
1031 if (!message.target())
1034 if (!this._view._showAllMessagesCheckbox.checked() && executionContext) {
1035 if (message.target() !== executionContext.target())
1037 if (message.executionContextId && message.executionContextId !== executionContext.id) {
1042 if (viewMessage.consoleMessage().isGroupMessage())
1045 if (message.type === WebInspector.ConsoleMessage.MessageType.Result || message.type === WebInspector.ConsoleMessage.MessageType.Command)
1048 if (message.url && this._messageURLFilters[message.url])
1051 if (message.level && !this._levelFilterUI.accept(message.level))
1054 if (this._filterRegex) {
1055 this._filterRegex.lastIndex = 0;
1056 if (!viewMessage.matchesRegex(this._filterRegex))
1065 this._messageURLFilters = {};
1066 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
1067 WebInspector.settings.messageLevelFilters.set({});
1068 this._view._showAllMessagesCheckbox.inputElement.checked = true;
1069 this._textFilterUI.setValue("");
1070 this._filterChanged();
1073 __proto__: WebInspector.Object.prototype
1079 * @extends {WebInspector.ConsoleViewMessage}
1080 * @param {!WebInspector.ConsoleMessage} message
1081 * @param {number} nestingLevel
1083 WebInspector.ConsoleCommand = function(message, nestingLevel)
1085 WebInspector.ConsoleViewMessage.call(this, message, null, nestingLevel);
1088 WebInspector.ConsoleCommand.prototype = {
1089 clearHighlight: function()
1091 var highlightedMessage = this._formattedCommand;
1092 delete this._formattedCommand;
1093 this._formatCommand();
1094 this._element.replaceChild(this._formattedCommand, highlightedMessage);
1098 * @param {!RegExp} regexObject
1100 highlightSearchResults: function(regexObject)
1102 regexObject.lastIndex = 0;
1103 var match = regexObject.exec(this.text);
1104 var matchRanges = [];
1106 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
1107 match = regexObject.exec(this.text);
1109 WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
1110 this._element.scrollIntoViewIfNeeded();
1114 * @param {!RegExp} regexObject
1117 matchesRegex: function(regexObject)
1119 regexObject.lastIndex = 0;
1120 return regexObject.test(this.text);
1124 * @return {!Element}
1126 contentElement: function()
1128 if (!this._element) {
1129 this._element = document.createElementWithClass("div", "console-user-command");
1130 this._element.message = this;
1132 this._formatCommand();
1133 this._element.appendChild(this._formattedCommand);
1135 return this._element;
1138 _formatCommand: function()
1140 this._formattedCommand = document.createElementWithClass("span", "console-message-text source-code");
1141 this._formattedCommand.textContent = this.text;
1144 __proto__: WebInspector.ConsoleViewMessage.prototype
1149 * @extends {WebInspector.ConsoleViewMessage}
1150 * @param {!WebInspector.ConsoleMessage} message
1151 * @param {!WebInspector.Linkifier} linkifier
1152 * @param {number} nestingLevel
1154 WebInspector.ConsoleCommandResult = function(message, linkifier, nestingLevel)
1156 WebInspector.ConsoleViewMessage.call(this, message, linkifier, nestingLevel);
1159 WebInspector.ConsoleCommandResult.prototype = {
1162 * @param {!WebInspector.RemoteObject} array
1165 useArrayPreviewInFormatter: function(array)
1171 * @return {!Element}
1173 contentElement: function()
1175 var element = WebInspector.ConsoleViewMessage.prototype.contentElement.call(this);
1176 element.classList.add("console-user-command-result");
1180 __proto__: WebInspector.ConsoleViewMessage.prototype
1185 * @param {?WebInspector.ConsoleGroup} parentGroup
1186 * @param {?WebInspector.ConsoleViewMessage} groupMessage
1188 WebInspector.ConsoleGroup = function(parentGroup, groupMessage)
1190 this._parentGroup = parentGroup;
1191 this._nestingLevel = parentGroup ? parentGroup.nestingLevel() + 1 : 0;
1192 this._messagesHidden = groupMessage && groupMessage.collapsed() || this._parentGroup && this._parentGroup.messagesHidden();
1196 * @return {!WebInspector.ConsoleGroup}
1198 WebInspector.ConsoleGroup.createTopGroup = function()
1200 return new WebInspector.ConsoleGroup(null, null);
1203 WebInspector.ConsoleGroup.prototype = {
1207 messagesHidden: function()
1209 return this._messagesHidden;
1215 nestingLevel: function()
1217 return this._nestingLevel;
1221 * @return {?WebInspector.ConsoleGroup}
1223 parentGroup: function()
1225 return this._parentGroup || this;
1231 * @implements {WebInspector.ActionDelegate}
1233 WebInspector.ConsoleView.ShowConsoleActionDelegate = function()
1237 WebInspector.ConsoleView.ShowConsoleActionDelegate.prototype = {
1241 handleAction: function()
1243 WebInspector.console.show();