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}
36 * @param {boolean} hideContextSelector
38 WebInspector.ConsoleView = function(hideContextSelector)
40 WebInspector.VBox.call(this);
41 this.registerRequiredCSS("filter.css");
43 this._searchableView = new WebInspector.SearchableView(this);
44 this._searchableView.setMinimalSearchQuerySize(0);
45 this._searchableView.show(this.element);
47 this._contentsElement = this._searchableView.element;
48 this._contentsElement.classList.add("console-view");
49 this._visibleViewMessages = [];
50 this._urlToMessageCount = {};
51 this._hiddenByFilterCount = 0;
53 this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item");
54 this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this);
56 this._executionContextSelector = new WebInspector.StatusBarComboBox(this._executionContextChanged.bind(this), "console-context");
59 * @type {!Map.<!WebInspector.ExecutionContext, !Element>}
61 this._optionByExecutionContext = new Map();
63 this._filter = new WebInspector.ConsoleViewFilter(this);
64 this._filter.addEventListener(WebInspector.ConsoleViewFilter.Events.FilterChanged, this._updateMessageList.bind(this));
66 if (hideContextSelector)
67 this._executionContextSelector.element.classList.add("hidden");
69 this._filterBar = new WebInspector.FilterBar();
71 var statusBarElement = this._contentsElement.createChild("div", "console-status-bar");
72 statusBarElement.appendChild(this._clearConsoleButton.element);
73 statusBarElement.appendChild(this._filterBar.filterButton().element);
74 statusBarElement.appendChild(this._executionContextSelector.element);
76 this._filtersContainer = this._contentsElement.createChild("div", "console-filters-header hidden");
77 this._filtersContainer.appendChild(this._filterBar.filtersElement());
78 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
79 this._filterBar.setName("consoleView");
80 this._filter.addFilters(this._filterBar);
82 this._viewport = new WebInspector.ViewportControl(this);
83 this._viewport.setStickToBottom(true);
84 this._viewport.contentElement().classList.add("console-group");
85 this._viewport.contentElement().classList.add("console-group-messages");
86 this._contentsElement.appendChild(this._viewport.element);
87 this._messagesElement = this._viewport.element;
88 this._messagesElement.id = "console-messages";
89 this._messagesElement.classList.add("monospace");
90 this._messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
91 this._scrolledToBottom = true;
93 this._filterStatusMessageElement = document.createElementWithClass("div", "console-message");
94 this._messagesElement.insertBefore(this._filterStatusMessageElement, this._messagesElement.firstChild);
95 this._filterStatusTextElement = this._filterStatusMessageElement.createChild("span", "console-info");
96 this._filterStatusMessageElement.createTextChild(" ");
97 var resetFiltersLink = this._filterStatusMessageElement.createChild("span", "console-info node-link");
98 resetFiltersLink.textContent = WebInspector.UIString("Show all messages.");
99 resetFiltersLink.addEventListener("click", this._filter.reset.bind(this._filter), true);
101 this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
102 this._currentGroup = this._topGroup;
104 this._promptElement = this._messagesElement.createChild("div", "source-code");
105 this._promptElement.id = "console-prompt";
106 this._promptElement.spellcheck = false;
107 this._messagesElement.appendChild(this._promptElement);
108 this._messagesElement.appendChild(document.createElement("br"));
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 if (!WebInspector.experimentsSettings.workersInMainWindow.isEnabled())
115 this._showAllMessagesCheckbox.element.classList.add("hidden");
117 statusBarElement.appendChild(this._showAllMessagesCheckbox.element);
119 this._registerShortcuts();
120 this.registerRequiredCSS("suggestBox.css");
122 this._messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
123 WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged, this);
125 this._linkifier = new WebInspector.Linkifier();
127 /** @type {!Array.<!WebInspector.ConsoleViewMessage>} */
128 this._consoleMessages = [];
130 this._prompt = new WebInspector.TextPromptWithHistory(WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext);
131 this._prompt.setSuggestBoxEnabled(true);
132 this._prompt.renderAsBlock();
133 this._prompt.attach(this._promptElement);
134 this._prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
135 this._prompt.setHistoryData(WebInspector.settings.consoleHistory.get());
136 var historyData = WebInspector.settings.consoleHistory.get();
137 this._prompt.setHistoryData(historyData);
139 this._updateFilterStatus();
140 WebInspector.settings.consoleTimestampsEnabled.addChangeListener(this._consoleTimestampsSettingChanged, this);
142 this._registerWithMessageSink();
143 WebInspector.targetManager.observeTargets(this);
144 WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executionContextChangedExternally, this);
147 WebInspector.ConsoleView.prototype = {
151 itemCount: function()
153 return this._visibleViewMessages.length;
157 * @param {number} index
158 * @return {?WebInspector.ViewportElement}
160 itemElement: function(index)
162 return this._visibleViewMessages[index];
166 * @param {number} index
169 fastHeight: function(index)
171 return this._visibleViewMessages[index].fastHeight();
177 minimumRowHeight: function()
183 * @param {!WebInspector.Target} target
185 targetAdded: function(target)
188 * @param {!WebInspector.ConsoleMessage} message
189 * @this {WebInspector.ConsoleView}
191 function appendMessage(message)
193 var viewMessage = this._createViewMessage(message);
194 this._consoleMessageAdded(viewMessage);
197 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
198 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
199 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
200 target.consoleModel.messages.forEach(appendMessage, this);
201 this._viewport.invalidate();
203 target.runtimeModel.executionContexts().forEach(this._executionContextCreated, this);
204 target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
205 target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
209 * @param {!WebInspector.Target} target
211 targetRemoved: function(target)
213 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
214 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
215 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
216 target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
217 target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
220 _registerWithMessageSink: function()
222 WebInspector.messageSink.messages().forEach(this._addSinkMessage, this);
223 WebInspector.messageSink.addEventListener(WebInspector.MessageSink.Events.MessageAdded, messageAdded, this);
226 * @param {!WebInspector.Event} event
227 * @this {WebInspector.ConsoleView}
229 function messageAdded(event)
231 this._addSinkMessage(/** @type {!WebInspector.MessageSink.Message} */ (event.data));
236 * @param {!WebInspector.MessageSink.Message} message
238 _addSinkMessage: function(message)
240 var level = WebInspector.ConsoleMessage.MessageLevel.Debug;
241 switch (message.level) {
242 case WebInspector.MessageSink.MessageLevel.Error:
243 level = WebInspector.ConsoleMessage.MessageLevel.Error;
245 case WebInspector.MessageSink.MessageLevel.Warning:
246 level = WebInspector.ConsoleMessage.MessageLevel.Warning;
250 var consoleMessage = new WebInspector.ConsoleMessage(null, WebInspector.ConsoleMessage.MessageSource.Other, level, message.text,
251 undefined, undefined, undefined, undefined, undefined, undefined, undefined, message.timestamp);
252 this._addConsoleMessage(consoleMessage);
256 * @param {!WebInspector.Event} event
258 _consoleTimestampsSettingChanged: function(event)
260 var enabled = /** @type {boolean} */ (event.data);
261 this._updateMessageList();
262 this._consoleMessages.forEach(function(viewMessage) {
263 viewMessage.updateTimestamp(enabled);
270 defaultFocusedElement: function()
272 return this._promptElement
275 _onFiltersToggled: function(event)
277 var toggled = /** @type {boolean} */ (event.data);
278 this._filtersContainer.classList.toggle("hidden", !toggled);
282 * @param {!WebInspector.ExecutionContext} executionContext
285 _titleFor: function(executionContext)
287 var result = executionContext.name;
288 if (executionContext.isMainWorldContext && executionContext.frameId) {
289 var frame = executionContext.target().resourceTreeModel.frameForId(executionContext.frameId);
290 result = frame ? frame.displayName() : result;
293 if (!executionContext.isMainWorldContext)
294 result = "\u00a0\u00a0\u00a0\u00a0" + result;
297 return result.trimMiddle(maxLength);
301 * @param {!WebInspector.Event} event
303 _onExecutionContextCreated: function(event)
305 var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
306 this._executionContextCreated(executionContext);
310 * @param {!WebInspector.ExecutionContext} executionContext
312 _executionContextCreated: function(executionContext)
314 var newOption = document.createElement("option");
315 newOption.__executionContext = executionContext;
316 newOption.text = this._titleFor(executionContext);
317 this._optionByExecutionContext.put(executionContext, newOption);
318 var sameGroupExists = false;
319 var options = this._executionContextSelector.selectElement().options;
320 var insertBeforeOption = null;
321 for (var i = 0; i < options.length; ++i) {
322 var optionContext = options[i].__executionContext;
323 var isSameGroup = executionContext.target() === optionContext.target() && executionContext.frameId === optionContext.frameId;
324 sameGroupExists |= isSameGroup;
325 if ((isSameGroup && WebInspector.ExecutionContext.comparator(optionContext, executionContext) > 0) || (sameGroupExists && !isSameGroup)) {
326 insertBeforeOption = options[i];
330 this._executionContextSelector.selectElement().insertBefore(newOption, insertBeforeOption);
331 if (executionContext === WebInspector.context.flavor(WebInspector.ExecutionContext))
332 this._executionContextSelector.select(newOption);
336 * @param {!WebInspector.Event} event
338 _onExecutionContextDestroyed: function(event)
340 var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
341 var option = this._optionByExecutionContext.remove(executionContext);
345 _executionContextChanged: function()
347 var newContext = this._currentExecutionContext();
348 WebInspector.context.setFlavor(WebInspector.ExecutionContext, newContext);
349 this._prompt.clearAutoComplete(true);
350 if (!this._showAllMessagesCheckbox.checked())
351 this._updateMessageList();
355 * @param {!WebInspector.Event} event
357 _executionContextChangedExternally: function(event)
359 var executionContext = /** @type {?WebInspector.ExecutionContext} */ (event.data);
360 if (!executionContext)
363 var options = this._executionContextSelector.selectElement().options;
364 for (var i = 0; i < options.length; ++i) {
365 if (options[i].__executionContext === executionContext)
366 this._executionContextSelector.select(options[i]);
371 * @return {?WebInspector.ExecutionContext}
373 _currentExecutionContext: function()
375 var option = this._executionContextSelector.selectedOption();
376 return option ? option.__executionContext : null;
381 this._prompt.hideSuggestBox();
382 this._prompt.clearAutoComplete(true);
387 this._viewport.refresh();
388 if (!this._prompt.isCaretInsidePrompt())
389 this._prompt.moveCaretToEndOfPrompt();
394 if (this._promptElement === WebInspector.currentFocusElement())
396 WebInspector.setCurrentFocusElement(this._promptElement);
397 this._prompt.moveCaretToEndOfPrompt();
400 storeScrollPositions: function()
402 WebInspector.View.prototype.storeScrollPositions.call(this);
403 this._scrolledToBottom = this._messagesElement.isScrolledToBottom();
406 restoreScrollPositions: function()
408 if (this._scrolledToBottom)
409 this._immediatelyScrollIntoView();
411 WebInspector.View.prototype.restoreScrollPositions.call(this);
416 this._scheduleViewportRefresh();
417 this._prompt.hideSuggestBox();
418 this.restoreScrollPositions();
421 _isScrollIntoViewScheduled: function()
423 return !!this._scrollIntoViewTimer;
426 _scheduleViewportRefresh: function()
428 if (this._scrollIntoViewTimer)
431 * @this {WebInspector.ConsoleView}
433 function scrollIntoView()
435 delete this._scrollIntoViewTimer;
436 this._viewport.invalidate();
438 this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 50);
441 _immediatelyScrollIntoView: function()
443 this._promptElement.scrollIntoView(true);
444 this._cancelScheduledScrollIntoView();
447 _cancelScheduledScrollIntoView: function()
449 if (!this._isScrollIntoViewScheduled())
451 clearTimeout(this._scrollIntoViewTimer);
452 this._viewport.refresh();
453 delete this._scrollIntoViewTimer;
456 _updateFilterStatus: function()
458 this._filterStatusTextElement.textContent = WebInspector.UIString(this._hiddenByFilterCount === 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", this._hiddenByFilterCount);
459 this._filterStatusMessageElement.style.display = this._hiddenByFilterCount ? "" : "none";
463 * @param {!WebInspector.ConsoleViewMessage} viewMessage
465 _consoleMessageAdded: function(viewMessage)
468 * @param {!WebInspector.ConsoleViewMessage} viewMessage1
469 * @param {!WebInspector.ConsoleViewMessage} viewMessage2
472 function compareTimestamps(viewMessage1, viewMessage2)
474 return WebInspector.ConsoleMessage.timestampComparator(viewMessage1.consoleMessage(), viewMessage2.consoleMessage());
476 var insertAt = insertionIndexForObjectInListSortedByFunction(viewMessage, this._consoleMessages, compareTimestamps, true);
477 this._consoleMessages.splice(insertAt, 0, viewMessage);
479 var message = viewMessage.consoleMessage();
480 if (this._urlToMessageCount[message.url])
481 this._urlToMessageCount[message.url]++;
483 this._urlToMessageCount[message.url] = 1;
485 if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
488 if (this._filter.shouldBeVisible(viewMessage))
489 this._showConsoleMessage(viewMessage)
491 this._hiddenByFilterCount++;
492 this._updateFilterStatus();
497 * @param {!WebInspector.Event} event
499 _onConsoleMessageAdded: function(event)
501 var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data);
502 this._addConsoleMessage(message);
506 * @param {!WebInspector.ConsoleMessage} message
508 _addConsoleMessage: function(message)
510 var viewMessage = this._createViewMessage(message);
511 this._consoleMessageAdded(viewMessage);
512 this._scheduleViewportRefresh();
516 * @param {!WebInspector.ConsoleViewMessage} viewMessage
518 _showConsoleMessage: function(viewMessage)
520 var lastMessage = this._visibleViewMessages.peekLast();
521 if (viewMessage.consoleMessage().type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
522 if (lastMessage && !this._currentGroup.messagesHidden())
523 lastMessage.incrementCloseGroupDecorationCount();
524 this._currentGroup = this._currentGroup.parentGroup();
527 if (!this._currentGroup.messagesHidden()) {
528 var originatingMessage = viewMessage.consoleMessage().originatingMessage();
529 if (lastMessage && originatingMessage && lastMessage.consoleMessage() === originatingMessage)
530 lastMessage.toMessageElement().classList.add("console-adjacent-user-command-result");
532 this._visibleViewMessages.push(viewMessage);
534 if (this._searchRegex && viewMessage.matchesRegex(this._searchRegex)) {
535 this._searchResults.push(viewMessage);
536 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
540 if (viewMessage.consoleMessage().isGroupStartMessage())
541 this._currentGroup = new WebInspector.ConsoleGroup(this._currentGroup, viewMessage);
545 * @param {!WebInspector.ConsoleMessage} message
546 * @return {!WebInspector.ConsoleViewMessage}
548 _createViewMessage: function(message)
550 var nestingLevel = this._currentGroup.nestingLevel();
551 switch (message.type) {
552 case WebInspector.ConsoleMessage.MessageType.Command:
553 return new WebInspector.ConsoleCommand(message, nestingLevel);
554 case WebInspector.ConsoleMessage.MessageType.Result:
555 return new WebInspector.ConsoleCommandResult(message, this._linkifier, nestingLevel);
556 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
557 case WebInspector.ConsoleMessage.MessageType.StartGroup:
558 return new WebInspector.ConsoleGroupViewMessage(message, this._linkifier, nestingLevel);
560 return new WebInspector.ConsoleViewMessage(message, this._linkifier, nestingLevel);
564 _consoleCleared: function()
566 this._clearCurrentSearchResultHighlight();
567 this._consoleMessages = [];
568 this._scrolledToBottom = true;
569 this._updateMessageList();
571 if (this._searchRegex)
572 this._searchableView.updateSearchMatchesCount(0);
574 this._linkifier.reset();
577 _handleContextMenuEvent: function(event)
579 if (event.target.enclosingNodeOrSelfWithNodeName("a"))
582 var contextMenu = new WebInspector.ContextMenu(event);
584 function monitoringXHRItemAction()
586 WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
588 contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction, WebInspector.settings.monitoringXHREnabled.get());
590 function preserveLogItemAction()
592 WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get());
594 contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction, WebInspector.settings.preserveConsoleLog.get());
596 var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message-wrapper");
597 var consoleMessage = sourceElement ? sourceElement.message.consoleMessage() : null;
599 var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
601 if (consoleMessage && consoleMessage.url) {
602 var menuTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(consoleMessage.url).displayName);
603 filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
606 filterSubMenu.appendSeparator();
607 var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._filter.removeMessageURLFilter.bind(this._filter));
608 filterSubMenu.appendSeparator();
610 var hasFilters = false;
612 for (var url in this._filter.messageURLFilters) {
613 filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._filter.removeMessageURLFilter.bind(this._filter, url), true);
617 filterSubMenu.setEnabled(hasFilters || (consoleMessage && consoleMessage.url));
618 unhideAll.setEnabled(hasFilters);
620 contextMenu.appendSeparator();
621 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
623 var request = consoleMessage ? consoleMessage.request : null;
624 if (request && request.type === WebInspector.resourceTypes.XHR) {
625 contextMenu.appendSeparator();
626 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
633 * @param {!WebInspector.ConsoleViewMessage} lastMessage
634 * @param {?WebInspector.ConsoleViewMessage} viewMessage
637 _tryToCollapseMessages: function(lastMessage, viewMessage)
639 if (!WebInspector.settings.consoleTimestampsEnabled.get() && viewMessage && !lastMessage.consoleMessage().isGroupMessage() && lastMessage.consoleMessage().isEqual(viewMessage.consoleMessage())) {
640 viewMessage.incrementRepeatCount();
647 _updateMessageList: function()
649 this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
650 this._currentGroup = this._topGroup;
651 this._searchResults = [];
652 this._hiddenByFilterCount = 0;
653 for (var i = 0; i < this._visibleViewMessages.length; ++i) {
654 this._visibleViewMessages[i].resetCloseGroupDecorationCount();
655 this._visibleViewMessages[i].resetIncrementRepeatCount();
657 this._visibleViewMessages = [];
658 for (var i = 0; i < this._consoleMessages.length; ++i) {
659 var viewMessage = this._consoleMessages[i];
660 if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
662 if (this._filter.shouldBeVisible(viewMessage))
663 this._showConsoleMessage(viewMessage);
665 this._hiddenByFilterCount++;
667 this._updateFilterStatus();
668 this._viewport.invalidate();
672 * @param {!WebInspector.Event} event
674 _monitoringXHREnabledSettingChanged: function(event)
676 var enabled = /** @type {boolean} */ (event.data);
677 WebInspector.targetManager.targets().forEach(function(target) {target.consoleAgent().setMonitoringXHREnabled(enabled);});
681 * @param {?Event} event
683 _messagesClicked: function(event)
685 if (!this._prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
686 this._prompt.moveCaretToEndOfPrompt();
687 var groupMessage = event.target.enclosingNodeOrSelfWithClass("console-group-title");
690 var consoleGroupViewMessage = groupMessage.parentElement.message;
691 consoleGroupViewMessage.setCollapsed(!consoleGroupViewMessage.collapsed());
692 this._updateMessageList();
695 _registerShortcuts: function()
697 this._shortcuts = {};
699 var shortcut = WebInspector.KeyboardShortcut;
700 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
702 var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
703 this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
704 var keys = [shortcutL];
705 if (WebInspector.isMac()) {
706 var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
707 this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
708 keys.unshift(shortcutK);
710 section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
712 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
713 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
715 var shortcutU = shortcut.makeDescriptor("u", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
716 this._shortcuts[shortcutU.key] = this._clearPromptBackwards.bind(this);
717 section.addAlternateKeys([shortcutU], WebInspector.UIString("Clear console prompt"));
720 shortcut.makeDescriptor(shortcut.Keys.Down),
721 shortcut.makeDescriptor(shortcut.Keys.Up)
723 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
725 if (WebInspector.isMac()) {
727 shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
728 shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
730 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
733 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
736 _clearPromptBackwards: function()
738 this._prompt.text = "";
741 _requestClearMessages: function()
743 WebInspector.console.requestClearMessages();
746 _promptKeyDown: function(event)
748 if (isEnterKey(event)) {
749 this._enterKeyPressed(event);
753 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
754 var handler = this._shortcuts[shortcut];
757 event.preventDefault();
761 _enterKeyPressed: function(event)
763 if (event.altKey || event.ctrlKey || event.shiftKey)
768 this._prompt.clearAutoComplete(true);
770 var str = this._prompt.text;
773 this._appendCommand(str, true);
777 * @param {?WebInspector.RemoteObject} result
778 * @param {boolean} wasThrown
779 * @param {!WebInspector.ConsoleMessage} originatingConsoleMessage
781 _printResult: function(result, wasThrown, originatingConsoleMessage)
786 var target = result.target();
788 * @param {string=} url
789 * @param {number=} lineNumber
790 * @param {number=} columnNumber
792 function addMessage(url, lineNumber, columnNumber)
794 var level = wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
795 var message = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, url, lineNumber, columnNumber, undefined, [result]);
796 message.setOriginatingMessage(originatingConsoleMessage);
797 target.consoleModel.addMessage(message);
800 if (result.type !== "function") {
805 result.functionDetails(didGetDetails);
808 * @param {?DebuggerAgent.FunctionDetails} response
810 function didGetDetails(response)
820 var script = target.debuggerModel.scriptForId(response.location.scriptId);
821 if (script && script.sourceURL) {
822 url = script.sourceURL;
823 lineNumber = response.location.lineNumber + 1;
824 columnNumber = response.location.columnNumber + 1;
826 // FIXME: this should be using live location.
827 addMessage(url, lineNumber, columnNumber);
832 * @param {string} text
833 * @param {boolean} useCommandLineAPI
835 _appendCommand: function(text, useCommandLineAPI)
838 this._prompt.text = "";
839 var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
840 if (currentExecutionContext)
841 WebInspector.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, text, useCommandLineAPI);
845 * @param {!WebInspector.Event} event
847 _commandEvaluated: function(event)
849 var data = /**{{result: ?WebInspector.RemoteObject, wasThrown: boolean, text: string, commandMessage: !WebInspector.ConsoleMessage}} */ (event.data);
850 this._prompt.pushHistoryItem(data.text);
851 WebInspector.settings.consoleHistory.set(this._prompt.historyData.slice(-30));
852 this._printResult(data.result, data.wasThrown, data.commandMessage);
856 * @return {!Array.<!Element>}
858 elementsToRestoreScrollPositionsFor: function()
860 return [this._messagesElement];
863 searchCanceled: function()
865 this._clearCurrentSearchResultHighlight();
866 delete this._searchResults;
867 delete this._searchRegex;
868 this._viewport.refresh();
872 * @param {string} query
873 * @param {boolean} shouldJump
874 * @param {boolean=} jumpBackwards
876 performSearch: function(query, shouldJump, jumpBackwards)
878 this.searchCanceled();
879 this._searchableView.updateSearchMatchesCount(0);
880 this._searchRegex = createPlainTextSearchRegex(query, "gi");
882 /** @type {!Array.<number>} */
883 this._searchResults = [];
884 for (var i = 0; i < this._visibleViewMessages.length; i++) {
885 if (this._visibleViewMessages[i].matchesRegex(this._searchRegex))
886 this._searchResults.push(i);
888 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
889 this._currentSearchResultIndex = -1;
890 if (shouldJump && this._searchResults.length)
891 this._jumpToSearchResult(jumpBackwards ? -1 : 0);
892 this._viewport.refresh();
895 jumpToNextSearchResult: function()
897 if (!this._searchResults || !this._searchResults.length)
899 this._jumpToSearchResult(this._currentSearchResultIndex + 1);
902 jumpToPreviousSearchResult: function()
904 if (!this._searchResults || !this._searchResults.length)
906 this._jumpToSearchResult(this._currentSearchResultIndex - 1);
909 _clearCurrentSearchResultHighlight: function()
911 if (!this._searchResults)
914 var highlightedViewMessage = this._visibleViewMessages[this._searchResults[this._currentSearchResultIndex]];
915 if (highlightedViewMessage)
916 highlightedViewMessage.clearHighlight();
917 this._currentSearchResultIndex = -1;
920 _jumpToSearchResult: function(index)
922 index = mod(index, this._searchResults.length);
923 this._clearCurrentSearchResultHighlight();
924 this._currentSearchResultIndex = index;
925 this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex);
926 var currentViewMessageIndex = this._searchResults[index];
927 this._viewport.scrollItemIntoView(currentViewMessageIndex);
928 this._visibleViewMessages[currentViewMessageIndex].highlightSearchResults(this._searchRegex);
931 __proto__: WebInspector.VBox.prototype
936 * @extends {WebInspector.Object}
937 * @param {!WebInspector.ConsoleView} view
939 WebInspector.ConsoleViewFilter = function(view)
942 this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
943 this._filterChanged = this.dispatchEventToListeners.bind(this, WebInspector.ConsoleViewFilter.Events.FilterChanged);
946 WebInspector.ConsoleViewFilter.Events = {
947 FilterChanged: "FilterChanged"
950 WebInspector.ConsoleViewFilter.prototype = {
951 addFilters: function(filterBar)
953 this._textFilterUI = new WebInspector.TextFilterUI(true);
954 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
955 filterBar.addFilter(this._textFilterUI);
958 {name: "error", label: WebInspector.UIString("Errors")},
959 {name: "warning", label: WebInspector.UIString("Warnings")},
960 {name: "info", label: WebInspector.UIString("Info")},
961 {name: "log", label: WebInspector.UIString("Logs")},
962 {name: "debug", label: WebInspector.UIString("Debug")}
964 this._levelFilterUI = new WebInspector.NamedBitSetFilterUI(levels, WebInspector.settings.messageLevelFilters);
965 this._levelFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
966 filterBar.addFilter(this._levelFilterUI);
969 _textFilterChanged: function(event)
971 this._filterRegex = this._textFilterUI.regex();
973 this._filterChanged();
977 * @param {string} url
979 addMessageURLFilter: function(url)
981 this._messageURLFilters[url] = true;
982 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
983 this._filterChanged();
987 * @param {string} url
989 removeMessageURLFilter: function(url)
992 this._messageURLFilters = {};
994 delete this._messageURLFilters[url];
996 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
997 this._filterChanged();
1001 * @returns {!Object}
1003 get messageURLFilters()
1005 return this._messageURLFilters;
1009 * @param {!WebInspector.ConsoleViewMessage} viewMessage
1012 shouldBeVisible: function(viewMessage)
1014 var message = viewMessage.consoleMessage();
1015 var executionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
1016 if (!message.target())
1019 if (!this._view._showAllMessagesCheckbox.checked() && executionContext && (message.target() !== executionContext.target() || message.executionContextId !== executionContext.id))
1022 if (viewMessage.consoleMessage().isGroupMessage())
1025 if (message.type === WebInspector.ConsoleMessage.MessageType.Result || message.type === WebInspector.ConsoleMessage.MessageType.Command)
1028 if (message.url && this._messageURLFilters[message.url])
1031 if (message.level && !this._levelFilterUI.accept(message.level))
1034 if (this._filterRegex) {
1035 this._filterRegex.lastIndex = 0;
1036 if (!viewMessage.matchesRegex(this._filterRegex))
1045 this._messageURLFilters = {};
1046 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
1047 WebInspector.settings.messageLevelFilters.set({});
1048 this._view._showAllMessagesCheckbox.inputElement.checked = true;
1049 this._textFilterUI.setValue("");
1050 this._filterChanged();
1053 __proto__: WebInspector.Object.prototype
1059 * @extends {WebInspector.ConsoleViewMessage}
1060 * @param {!WebInspector.ConsoleMessage} message
1061 * @param {number} nestingLevel
1063 WebInspector.ConsoleCommand = function(message, nestingLevel)
1065 WebInspector.ConsoleViewMessage.call(this, message, null, nestingLevel);
1068 WebInspector.ConsoleCommand.prototype = {
1069 clearHighlight: function()
1071 var highlightedMessage = this._formattedCommand;
1072 delete this._formattedCommand;
1073 this._formatCommand();
1074 this._element.replaceChild(this._formattedCommand, highlightedMessage);
1078 * @param {!RegExp} regexObject
1080 highlightSearchResults: function(regexObject)
1082 regexObject.lastIndex = 0;
1083 var match = regexObject.exec(this.text);
1084 var matchRanges = [];
1086 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
1087 match = regexObject.exec(this.text);
1089 WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
1090 this._element.scrollIntoViewIfNeeded();
1094 * @param {!RegExp} regexObject
1097 matchesRegex: function(regexObject)
1099 regexObject.lastIndex = 0;
1100 return regexObject.test(this.text);
1104 * @return {!Element}
1106 contentElement: function()
1108 if (!this._element) {
1109 this._element = document.createElement("div");
1110 this._element.message = this;
1111 this._element.className = "console-user-command";
1113 this._formatCommand();
1114 this._element.appendChild(this._formattedCommand);
1116 return this._element;
1119 _formatCommand: function()
1121 this._formattedCommand = document.createElement("span");
1122 this._formattedCommand.className = "console-message-text source-code";
1123 this._formattedCommand.textContent = this.text;
1126 __proto__: WebInspector.ConsoleViewMessage.prototype
1131 * @extends {WebInspector.ConsoleViewMessage}
1132 * @param {!WebInspector.ConsoleMessage} message
1133 * @param {!WebInspector.Linkifier} linkifier
1134 * @param {number} nestingLevel
1136 WebInspector.ConsoleCommandResult = function(message, linkifier, nestingLevel)
1138 WebInspector.ConsoleViewMessage.call(this, message, linkifier, nestingLevel);
1141 WebInspector.ConsoleCommandResult.prototype = {
1144 * @param {!WebInspector.RemoteObject} array
1147 useArrayPreviewInFormatter: function(array)
1153 * @return {!Element}
1155 contentElement: function()
1157 var element = WebInspector.ConsoleViewMessage.prototype.contentElement.call(this);
1158 element.classList.add("console-user-command-result");
1162 __proto__: WebInspector.ConsoleViewMessage.prototype
1167 * @param {?WebInspector.ConsoleGroup} parentGroup
1168 * @param {?WebInspector.ConsoleViewMessage} groupMessage
1170 WebInspector.ConsoleGroup = function(parentGroup, groupMessage)
1172 this._parentGroup = parentGroup;
1173 this._nestingLevel = parentGroup ? parentGroup.nestingLevel() + 1 : 0;
1174 this._messagesHidden = groupMessage && groupMessage.collapsed() || this._parentGroup && this._parentGroup.messagesHidden();
1178 * @return {!WebInspector.ConsoleGroup}
1180 WebInspector.ConsoleGroup.createTopGroup = function()
1182 return new WebInspector.ConsoleGroup(null, null);
1185 WebInspector.ConsoleGroup.prototype = {
1189 messagesHidden: function()
1191 return this._messagesHidden;
1197 nestingLevel: function()
1199 return this._nestingLevel;
1203 * @return {?WebInspector.ConsoleGroup}
1205 parentGroup: function()
1207 return this._parentGroup || this;
1213 * @implements {WebInspector.ActionDelegate}
1215 WebInspector.ConsoleView.ShowConsoleActionDelegate = function()
1219 WebInspector.ConsoleView.ShowConsoleActionDelegate.prototype = {
1223 handleAction: function()
1225 WebInspector.console.show();