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();
175 * @param {!WebInspector.Target} target
177 targetAdded: function(target)
180 * @param {!WebInspector.ConsoleMessage} message
181 * @this {WebInspector.ConsoleView}
183 function appendMessage(message)
185 var viewMessage = this._createViewMessage(message);
186 this._consoleMessageAdded(viewMessage);
189 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
190 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
191 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
192 target.consoleModel.messages.forEach(appendMessage, this);
193 this._viewport.invalidate();
195 target.runtimeModel.executionContexts().forEach(this._executionContextCreated, this);
196 target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
197 target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
201 * @param {!WebInspector.Target} target
203 targetRemoved: function(target)
205 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this);
206 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
207 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
208 target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this);
209 target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this);
212 _registerWithMessageSink: function()
214 WebInspector.messageSink.messages().forEach(this._addSinkMessage, this);
215 WebInspector.messageSink.addEventListener(WebInspector.MessageSink.Events.MessageAdded, messageAdded, this);
218 * @param {!WebInspector.Event} event
219 * @this {WebInspector.ConsoleView}
221 function messageAdded(event)
223 this._addSinkMessage(/** @type {!WebInspector.MessageSink.Message} */ (event.data));
228 * @param {!WebInspector.MessageSink.Message} message
230 _addSinkMessage: function(message)
232 var level = WebInspector.ConsoleMessage.MessageLevel.Debug;
233 switch (message.level) {
234 case WebInspector.MessageSink.MessageLevel.Error:
235 level = WebInspector.ConsoleMessage.MessageLevel.Error;
237 case WebInspector.MessageSink.MessageLevel.Warning:
238 level = WebInspector.ConsoleMessage.MessageLevel.Warning;
242 var consoleMessage = new WebInspector.ConsoleMessage(null, WebInspector.ConsoleMessage.MessageSource.Other, level, message.text,
243 undefined, undefined, undefined, undefined, undefined, undefined, undefined, message.timestamp);
244 this._addConsoleMessage(consoleMessage);
248 * @param {!WebInspector.Event} event
250 _consoleTimestampsSettingChanged: function(event)
252 var enabled = /** @type {boolean} */ (event.data);
253 this._updateMessageList();
254 this._consoleMessages.forEach(function(viewMessage) {
255 viewMessage.updateTimestamp(enabled);
262 defaultFocusedElement: function()
264 return this._promptElement
267 _onFiltersToggled: function(event)
269 var toggled = /** @type {boolean} */ (event.data);
270 this._filtersContainer.classList.toggle("hidden", !toggled);
274 * @param {!WebInspector.ExecutionContext} executionContext
277 _titleFor: function(executionContext)
279 var result = executionContext.name;
280 if (executionContext.isMainWorldContext && executionContext.frameId) {
281 var frame = executionContext.target().resourceTreeModel.frameForId(executionContext.frameId);
282 result = frame ? frame.displayName() : result;
285 if (!executionContext.isMainWorldContext)
286 result = "\u00a0\u00a0\u00a0\u00a0" + result;
289 return result.trimMiddle(maxLength);
293 * @param {!WebInspector.Event} event
295 _onExecutionContextCreated: function(event)
297 var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
298 this._executionContextCreated(executionContext);
302 * @param {!WebInspector.ExecutionContext} executionContext
304 _executionContextCreated: function(executionContext)
306 var newOption = document.createElement("option");
307 newOption.__executionContext = executionContext;
308 newOption.text = this._titleFor(executionContext);
309 this._optionByExecutionContext.put(executionContext, newOption);
310 var sameGroupExists = false;
311 var options = this._executionContextSelector.selectElement().options;
312 var insertBeforeOption = null;
313 for (var i = 0; i < options.length; ++i) {
314 var optionContext = options[i].__executionContext;
315 var isSameGroup = executionContext.target() === optionContext.target() && executionContext.frameId === optionContext.frameId;
316 sameGroupExists |= isSameGroup;
317 if ((isSameGroup && WebInspector.ExecutionContext.comparator(optionContext, executionContext) > 0) || (sameGroupExists && !isSameGroup)) {
318 insertBeforeOption = options[i];
322 this._executionContextSelector.selectElement().insertBefore(newOption, insertBeforeOption);
323 if (executionContext === WebInspector.context.flavor(WebInspector.ExecutionContext))
324 this._executionContextSelector.select(newOption);
328 * @param {!WebInspector.Event} event
330 _onExecutionContextDestroyed: function(event)
332 var executionContext = /** @type {!WebInspector.ExecutionContext} */ (event.data);
333 var option = this._optionByExecutionContext.remove(executionContext);
337 _executionContextChanged: function()
339 var newContext = this._currentExecutionContext();
340 WebInspector.context.setFlavor(WebInspector.ExecutionContext, newContext);
341 this._prompt.clearAutoComplete(true);
342 if (!this._showAllMessagesCheckbox.checked())
343 this._updateMessageList();
347 * @param {!WebInspector.Event} event
349 _executionContextChangedExternally: function(event)
351 var executionContext = /** @type {?WebInspector.ExecutionContext} */ (event.data);
352 if (!executionContext)
355 var options = this._executionContextSelector.selectElement().options;
356 for (var i = 0; i < options.length; ++i) {
357 if (options[i].__executionContext === executionContext)
358 this._executionContextSelector.select(options[i]);
363 * @return {?WebInspector.ExecutionContext}
365 _currentExecutionContext: function()
367 var option = this._executionContextSelector.selectedOption();
368 return option ? option.__executionContext : null;
373 this._prompt.hideSuggestBox();
374 this._prompt.clearAutoComplete(true);
379 this._viewport.refresh();
380 if (!this._prompt.isCaretInsidePrompt())
381 this._prompt.moveCaretToEndOfPrompt();
386 if (this._promptElement === WebInspector.currentFocusElement())
388 WebInspector.setCurrentFocusElement(this._promptElement);
389 this._prompt.moveCaretToEndOfPrompt();
392 storeScrollPositions: function()
394 WebInspector.View.prototype.storeScrollPositions.call(this);
395 this._scrolledToBottom = this._messagesElement.isScrolledToBottom();
398 restoreScrollPositions: function()
400 if (this._scrolledToBottom)
401 this._immediatelyScrollIntoView();
403 WebInspector.View.prototype.restoreScrollPositions.call(this);
408 this._scheduleViewportRefresh();
409 this._prompt.hideSuggestBox();
410 this.restoreScrollPositions();
413 _isScrollIntoViewScheduled: function()
415 return !!this._scrollIntoViewTimer;
418 _scheduleViewportRefresh: function()
420 if (this._scrollIntoViewTimer)
423 * @this {WebInspector.ConsoleView}
425 function scrollIntoView()
427 delete this._scrollIntoViewTimer;
428 this._viewport.invalidate();
430 this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 50);
433 _immediatelyScrollIntoView: function()
435 this._promptElement.scrollIntoView(true);
436 this._cancelScheduledScrollIntoView();
439 _cancelScheduledScrollIntoView: function()
441 if (!this._isScrollIntoViewScheduled())
443 clearTimeout(this._scrollIntoViewTimer);
444 this._viewport.refresh();
445 delete this._scrollIntoViewTimer;
448 _updateFilterStatus: function()
450 this._filterStatusTextElement.textContent = WebInspector.UIString(this._hiddenByFilterCount === 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", this._hiddenByFilterCount);
451 this._filterStatusMessageElement.style.display = this._hiddenByFilterCount ? "" : "none";
455 * @param {!WebInspector.ConsoleViewMessage} viewMessage
457 _consoleMessageAdded: function(viewMessage)
460 * @param {!WebInspector.ConsoleViewMessage} viewMessage1
461 * @param {!WebInspector.ConsoleViewMessage} viewMessage2
464 function compareTimestamps(viewMessage1, viewMessage2)
466 return WebInspector.ConsoleMessage.timestampComparator(viewMessage1.consoleMessage(), viewMessage2.consoleMessage());
468 var insertAt = insertionIndexForObjectInListSortedByFunction(viewMessage, this._consoleMessages, compareTimestamps, true);
469 this._consoleMessages.splice(insertAt, 0, viewMessage);
471 var message = viewMessage.consoleMessage();
472 if (this._urlToMessageCount[message.url])
473 this._urlToMessageCount[message.url]++;
475 this._urlToMessageCount[message.url] = 1;
477 if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
480 if (this._filter.shouldBeVisible(viewMessage))
481 this._showConsoleMessage(viewMessage)
483 this._hiddenByFilterCount++;
484 this._updateFilterStatus();
489 * @param {!WebInspector.Event} event
491 _onConsoleMessageAdded: function(event)
493 var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data);
494 this._addConsoleMessage(message);
498 * @param {!WebInspector.ConsoleMessage} message
500 _addConsoleMessage: function(message)
502 var viewMessage = this._createViewMessage(message);
503 this._consoleMessageAdded(viewMessage);
504 this._scheduleViewportRefresh();
508 * @param {!WebInspector.ConsoleViewMessage} viewMessage
510 _showConsoleMessage: function(viewMessage)
512 var lastMessage = this._visibleViewMessages.peekLast();
513 if (viewMessage.consoleMessage().type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
514 if (lastMessage && !this._currentGroup.messagesHidden())
515 lastMessage.incrementCloseGroupDecorationCount();
516 this._currentGroup = this._currentGroup.parentGroup();
519 if (!this._currentGroup.messagesHidden()) {
520 var originatingMessage = viewMessage.consoleMessage().originatingMessage();
521 if (lastMessage && originatingMessage && lastMessage.consoleMessage() === originatingMessage)
522 lastMessage.toMessageElement().classList.add("console-adjacent-user-command-result");
524 this._visibleViewMessages.push(viewMessage);
526 if (this._searchRegex && viewMessage.matchesRegex(this._searchRegex)) {
527 this._searchResults.push(viewMessage);
528 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
532 if (viewMessage.consoleMessage().isGroupStartMessage())
533 this._currentGroup = new WebInspector.ConsoleGroup(this._currentGroup, viewMessage);
537 * @param {!WebInspector.ConsoleMessage} message
538 * @return {!WebInspector.ConsoleViewMessage}
540 _createViewMessage: function(message)
542 var nestingLevel = this._currentGroup.nestingLevel();
543 switch (message.type) {
544 case WebInspector.ConsoleMessage.MessageType.Command:
545 return new WebInspector.ConsoleCommand(message, nestingLevel);
546 case WebInspector.ConsoleMessage.MessageType.Result:
547 return new WebInspector.ConsoleCommandResult(message, this._linkifier, nestingLevel);
548 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
549 case WebInspector.ConsoleMessage.MessageType.StartGroup:
550 return new WebInspector.ConsoleGroupViewMessage(message, this._linkifier, nestingLevel);
552 return new WebInspector.ConsoleViewMessage(message, this._linkifier, nestingLevel);
556 _consoleCleared: function()
558 this._clearCurrentSearchResultHighlight();
559 this._consoleMessages = [];
560 this._scrolledToBottom = true;
561 this._updateMessageList();
563 if (this._searchRegex)
564 this._searchableView.updateSearchMatchesCount(0);
566 this._linkifier.reset();
569 _handleContextMenuEvent: function(event)
571 if (event.target.enclosingNodeOrSelfWithNodeName("a"))
574 var contextMenu = new WebInspector.ContextMenu(event);
576 function monitoringXHRItemAction()
578 WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
580 contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction, WebInspector.settings.monitoringXHREnabled.get());
582 function preserveLogItemAction()
584 WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get());
586 contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction, WebInspector.settings.preserveConsoleLog.get());
588 var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message-wrapper");
589 var consoleMessage = sourceElement ? sourceElement.message.consoleMessage() : null;
591 var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
593 if (consoleMessage && consoleMessage.url) {
594 var menuTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(consoleMessage.url).displayName);
595 filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, consoleMessage.url));
598 filterSubMenu.appendSeparator();
599 var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._filter.removeMessageURLFilter.bind(this._filter));
600 filterSubMenu.appendSeparator();
602 var hasFilters = false;
604 for (var url in this._filter.messageURLFilters) {
605 filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._filter.removeMessageURLFilter.bind(this._filter, url), true);
609 filterSubMenu.setEnabled(hasFilters || (consoleMessage && consoleMessage.url));
610 unhideAll.setEnabled(hasFilters);
612 contextMenu.appendSeparator();
613 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
615 var request = consoleMessage ? consoleMessage.request : null;
616 if (request && request.type === WebInspector.resourceTypes.XHR) {
617 contextMenu.appendSeparator();
618 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
625 * @param {!WebInspector.ConsoleViewMessage} lastMessage
626 * @param {?WebInspector.ConsoleViewMessage} viewMessage
629 _tryToCollapseMessages: function(lastMessage, viewMessage)
631 if (!WebInspector.settings.consoleTimestampsEnabled.get() && viewMessage && !lastMessage.consoleMessage().isGroupMessage() && lastMessage.consoleMessage().isEqual(viewMessage.consoleMessage())) {
632 viewMessage.incrementRepeatCount();
639 _updateMessageList: function()
641 this._topGroup = WebInspector.ConsoleGroup.createTopGroup();
642 this._currentGroup = this._topGroup;
643 this._searchResults = [];
644 this._hiddenByFilterCount = 0;
645 for (var i = 0; i < this._visibleViewMessages.length; ++i) {
646 this._visibleViewMessages[i].resetCloseGroupDecorationCount();
647 this._visibleViewMessages[i].resetIncrementRepeatCount();
649 this._visibleViewMessages = [];
650 for (var i = 0; i < this._consoleMessages.length; ++i) {
651 var viewMessage = this._consoleMessages[i];
652 if (this._tryToCollapseMessages(viewMessage, this._visibleViewMessages.peekLast()))
654 if (this._filter.shouldBeVisible(viewMessage))
655 this._showConsoleMessage(viewMessage);
657 this._hiddenByFilterCount++;
659 this._updateFilterStatus();
660 this._viewport.invalidate();
664 * @param {!WebInspector.Event} event
666 _monitoringXHREnabledSettingChanged: function(event)
668 var enabled = /** @type {boolean} */ (event.data);
669 WebInspector.targetManager.targets().forEach(function(target) {target.consoleAgent().setMonitoringXHREnabled(enabled);});
673 * @param {?Event} event
675 _messagesClicked: function(event)
677 if (!this._prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
678 this._prompt.moveCaretToEndOfPrompt();
679 var groupMessage = event.target.enclosingNodeOrSelfWithClass("console-group-title");
682 var consoleGroupViewMessage = groupMessage.parentElement.message;
683 consoleGroupViewMessage.setCollapsed(!consoleGroupViewMessage.collapsed());
684 this._updateMessageList();
687 _registerShortcuts: function()
689 this._shortcuts = {};
691 var shortcut = WebInspector.KeyboardShortcut;
692 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
694 var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
695 this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
696 var keys = [shortcutL];
697 if (WebInspector.isMac()) {
698 var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
699 this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
700 keys.unshift(shortcutK);
702 section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
704 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
705 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
707 var shortcutU = shortcut.makeDescriptor("u", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
708 this._shortcuts[shortcutU.key] = this._clearPromptBackwards.bind(this);
709 section.addAlternateKeys([shortcutU], WebInspector.UIString("Clear console prompt"));
712 shortcut.makeDescriptor(shortcut.Keys.Down),
713 shortcut.makeDescriptor(shortcut.Keys.Up)
715 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
717 if (WebInspector.isMac()) {
719 shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
720 shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
722 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
725 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
728 _clearPromptBackwards: function()
730 this._prompt.text = "";
733 _requestClearMessages: function()
735 WebInspector.console.requestClearMessages();
738 _promptKeyDown: function(event)
740 if (isEnterKey(event)) {
741 this._enterKeyPressed(event);
745 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
746 var handler = this._shortcuts[shortcut];
749 event.preventDefault();
753 _enterKeyPressed: function(event)
755 if (event.altKey || event.ctrlKey || event.shiftKey)
760 this._prompt.clearAutoComplete(true);
762 var str = this._prompt.text;
765 this._appendCommand(str, true);
769 * @param {?WebInspector.RemoteObject} result
770 * @param {boolean} wasThrown
771 * @param {!WebInspector.ConsoleMessage} originatingConsoleMessage
773 _printResult: function(result, wasThrown, originatingConsoleMessage)
778 var target = result.target();
780 * @param {string=} url
781 * @param {number=} lineNumber
782 * @param {number=} columnNumber
784 function addMessage(url, lineNumber, columnNumber)
786 var level = wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
787 var message = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, url, lineNumber, columnNumber, undefined, [result]);
788 message.setOriginatingMessage(originatingConsoleMessage);
789 target.consoleModel.addMessage(message);
792 if (result.type !== "function") {
797 result.functionDetails(didGetDetails);
800 * @param {?DebuggerAgent.FunctionDetails} response
802 function didGetDetails(response)
812 var script = target.debuggerModel.scriptForId(response.location.scriptId);
813 if (script && script.sourceURL) {
814 url = script.sourceURL;
815 lineNumber = response.location.lineNumber + 1;
816 columnNumber = response.location.columnNumber + 1;
818 // FIXME: this should be using live location.
819 addMessage(url, lineNumber, columnNumber);
824 * @param {string} text
825 * @param {boolean} useCommandLineAPI
827 _appendCommand: function(text, useCommandLineAPI)
830 this._prompt.text = "";
831 var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
832 if (currentExecutionContext)
833 WebInspector.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, text, useCommandLineAPI);
837 * @param {!WebInspector.Event} event
839 _commandEvaluated: function(event)
841 var data = /**{{result: ?WebInspector.RemoteObject, wasThrown: boolean, text: string, commandMessage: !WebInspector.ConsoleMessage}} */ (event.data);
842 this._prompt.pushHistoryItem(data.text);
843 WebInspector.settings.consoleHistory.set(this._prompt.historyData.slice(-30));
844 this._printResult(data.result, data.wasThrown, data.commandMessage);
848 * @return {!Array.<!Element>}
850 elementsToRestoreScrollPositionsFor: function()
852 return [this._messagesElement];
855 searchCanceled: function()
857 this._clearCurrentSearchResultHighlight();
858 delete this._searchResults;
859 delete this._searchRegex;
860 this._viewport.refresh();
864 * @param {string} query
865 * @param {boolean} shouldJump
866 * @param {boolean=} jumpBackwards
868 performSearch: function(query, shouldJump, jumpBackwards)
870 this.searchCanceled();
871 this._searchableView.updateSearchMatchesCount(0);
872 this._searchRegex = createPlainTextSearchRegex(query, "gi");
874 /** @type {!Array.<number>} */
875 this._searchResults = [];
876 for (var i = 0; i < this._visibleViewMessages.length; i++) {
877 if (this._visibleViewMessages[i].matchesRegex(this._searchRegex))
878 this._searchResults.push(i);
880 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
881 this._currentSearchResultIndex = -1;
882 if (shouldJump && this._searchResults.length)
883 this._jumpToSearchResult(jumpBackwards ? -1 : 0);
884 this._viewport.refresh();
887 jumpToNextSearchResult: function()
889 if (!this._searchResults || !this._searchResults.length)
891 this._jumpToSearchResult(this._currentSearchResultIndex + 1);
894 jumpToPreviousSearchResult: function()
896 if (!this._searchResults || !this._searchResults.length)
898 this._jumpToSearchResult(this._currentSearchResultIndex - 1);
901 _clearCurrentSearchResultHighlight: function()
903 if (!this._searchResults)
906 var highlightedViewMessage = this._visibleViewMessages[this._searchResults[this._currentSearchResultIndex]];
907 if (highlightedViewMessage)
908 highlightedViewMessage.clearHighlight();
909 this._currentSearchResultIndex = -1;
912 _jumpToSearchResult: function(index)
914 index = mod(index, this._searchResults.length);
915 this._clearCurrentSearchResultHighlight();
916 this._currentSearchResultIndex = index;
917 this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex);
918 var currentViewMessageIndex = this._searchResults[index];
919 this._viewport.scrollItemIntoView(currentViewMessageIndex);
920 this._visibleViewMessages[currentViewMessageIndex].highlightSearchResults(this._searchRegex);
923 __proto__: WebInspector.VBox.prototype
928 * @extends {WebInspector.Object}
929 * @param {!WebInspector.ConsoleView} view
931 WebInspector.ConsoleViewFilter = function(view)
934 this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
935 this._filterChanged = this.dispatchEventToListeners.bind(this, WebInspector.ConsoleViewFilter.Events.FilterChanged);
938 WebInspector.ConsoleViewFilter.Events = {
939 FilterChanged: "FilterChanged"
942 WebInspector.ConsoleViewFilter.prototype = {
943 addFilters: function(filterBar)
945 this._textFilterUI = new WebInspector.TextFilterUI(true);
946 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
947 filterBar.addFilter(this._textFilterUI);
950 {name: "error", label: WebInspector.UIString("Errors")},
951 {name: "warning", label: WebInspector.UIString("Warnings")},
952 {name: "info", label: WebInspector.UIString("Info")},
953 {name: "log", label: WebInspector.UIString("Logs")},
954 {name: "debug", label: WebInspector.UIString("Debug")}
956 this._levelFilterUI = new WebInspector.NamedBitSetFilterUI(levels, WebInspector.settings.messageLevelFilters);
957 this._levelFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
958 filterBar.addFilter(this._levelFilterUI);
961 _textFilterChanged: function(event)
963 this._filterRegex = this._textFilterUI.regex();
965 this._filterChanged();
969 * @param {string} url
971 addMessageURLFilter: function(url)
973 this._messageURLFilters[url] = true;
974 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
975 this._filterChanged();
979 * @param {string} url
981 removeMessageURLFilter: function(url)
984 this._messageURLFilters = {};
986 delete this._messageURLFilters[url];
988 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
989 this._filterChanged();
995 get messageURLFilters()
997 return this._messageURLFilters;
1001 * @param {!WebInspector.ConsoleViewMessage} viewMessage
1004 shouldBeVisible: function(viewMessage)
1006 var message = viewMessage.consoleMessage();
1007 var executionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
1008 if (!message.target())
1011 if (!this._view._showAllMessagesCheckbox.checked() && executionContext && (message.target() !== executionContext.target() || message.executionContextId !== executionContext.id))
1014 if (viewMessage.consoleMessage().isGroupMessage())
1017 if (message.type === WebInspector.ConsoleMessage.MessageType.Result || message.type === WebInspector.ConsoleMessage.MessageType.Command)
1020 if (message.url && this._messageURLFilters[message.url])
1023 if (message.level && !this._levelFilterUI.accept(message.level))
1026 if (this._filterRegex) {
1027 this._filterRegex.lastIndex = 0;
1028 if (!viewMessage.matchesRegex(this._filterRegex))
1037 this._messageURLFilters = {};
1038 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
1039 WebInspector.settings.messageLevelFilters.set({});
1040 this._view._showAllMessagesCheckbox.inputElement.checked = true;
1041 this._textFilterUI.setValue("");
1042 this._filterChanged();
1045 __proto__: WebInspector.Object.prototype
1051 * @extends {WebInspector.ConsoleViewMessage}
1052 * @param {!WebInspector.ConsoleMessage} message
1053 * @param {number} nestingLevel
1055 WebInspector.ConsoleCommand = function(message, nestingLevel)
1057 WebInspector.ConsoleViewMessage.call(this, message, null, nestingLevel);
1060 WebInspector.ConsoleCommand.prototype = {
1061 clearHighlight: function()
1063 var highlightedMessage = this._formattedCommand;
1064 delete this._formattedCommand;
1065 this._formatCommand();
1066 this._element.replaceChild(this._formattedCommand, highlightedMessage);
1070 * @param {!RegExp} regexObject
1072 highlightSearchResults: function(regexObject)
1074 regexObject.lastIndex = 0;
1075 var match = regexObject.exec(this.text);
1076 var matchRanges = [];
1078 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
1079 match = regexObject.exec(this.text);
1081 WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
1082 this._element.scrollIntoViewIfNeeded();
1086 * @param {!RegExp} regexObject
1089 matchesRegex: function(regexObject)
1091 regexObject.lastIndex = 0;
1092 return regexObject.test(this.text);
1096 * @return {!Element}
1098 contentElement: function()
1100 if (!this._element) {
1101 this._element = document.createElement("div");
1102 this._element.message = this;
1103 this._element.className = "console-user-command";
1105 this._formatCommand();
1106 this._element.appendChild(this._formattedCommand);
1108 return this._element;
1111 _formatCommand: function()
1113 this._formattedCommand = document.createElement("span");
1114 this._formattedCommand.className = "console-message-text source-code";
1115 this._formattedCommand.textContent = this.text;
1118 __proto__: WebInspector.ConsoleViewMessage.prototype
1123 * @extends {WebInspector.ConsoleViewMessage}
1124 * @param {!WebInspector.ConsoleMessage} message
1125 * @param {!WebInspector.Linkifier} linkifier
1126 * @param {number} nestingLevel
1128 WebInspector.ConsoleCommandResult = function(message, linkifier, nestingLevel)
1130 WebInspector.ConsoleViewMessage.call(this, message, linkifier, nestingLevel);
1133 WebInspector.ConsoleCommandResult.prototype = {
1136 * @param {!WebInspector.RemoteObject} array
1139 useArrayPreviewInFormatter: function(array)
1145 * @return {!Element}
1147 contentElement: function()
1149 var element = WebInspector.ConsoleViewMessage.prototype.contentElement.call(this);
1150 element.classList.add("console-user-command-result");
1154 __proto__: WebInspector.ConsoleViewMessage.prototype
1159 * @param {?WebInspector.ConsoleGroup} parentGroup
1160 * @param {?WebInspector.ConsoleViewMessage} groupMessage
1162 WebInspector.ConsoleGroup = function(parentGroup, groupMessage)
1164 this._parentGroup = parentGroup;
1165 this._nestingLevel = parentGroup ? parentGroup.nestingLevel() + 1 : 0;
1166 this._messagesHidden = groupMessage && groupMessage.collapsed() || this._parentGroup && this._parentGroup.messagesHidden();
1170 * @return {!WebInspector.ConsoleGroup}
1172 WebInspector.ConsoleGroup.createTopGroup = function()
1174 return new WebInspector.ConsoleGroup(null, null);
1177 WebInspector.ConsoleGroup.prototype = {
1181 messagesHidden: function()
1183 return this._messagesHidden;
1189 nestingLevel: function()
1191 return this._nestingLevel;
1195 * @return {?WebInspector.ConsoleGroup}
1197 parentGroup: function()
1199 return this._parentGroup || this;
1205 * @implements {WebInspector.ActionDelegate}
1207 WebInspector.ConsoleView.ShowConsoleActionDelegate = function()
1211 WebInspector.ConsoleView.ShowConsoleActionDelegate.prototype = {
1215 handleAction: function()
1217 WebInspector.console.show();