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.
31 * @extends {WebInspector.VBox}
32 * @implements {WebInspector.Searchable}
34 * @param {boolean} hideContextSelector
36 WebInspector.ConsoleView = function(hideContextSelector)
38 WebInspector.VBox.call(this);
39 this.registerRequiredCSS("filter.css");
41 this._searchableView = new WebInspector.SearchableView(this);
42 this._searchableView.setMinimalSearchQuerySize(0);
43 this._searchableView.show(this.element);
45 this._contentsElement = this._searchableView.element;
46 this._contentsElement.classList.add("console-view");
47 this._visibleViewMessages = [];
48 this._urlToMessageCount = {};
50 this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item");
51 this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this);
53 this._executionContextSelector = new WebInspector.StatusBarComboBox(this._executionContextChanged.bind(this), "console-context");
54 this._topLevelOptionByContextListId = {};
55 this._subOptionsByContextListId = {};
57 this._filter = new WebInspector.ConsoleViewFilter(this);
58 this._filter.addEventListener(WebInspector.ConsoleViewFilter.Events.FilterChanged, this._updateMessageList.bind(this));
60 if (hideContextSelector)
61 this._executionContextSelector.element.classList.add("hidden");
63 this._filterBar = new WebInspector.FilterBar();
65 var statusBarElement = this._contentsElement.createChild("div", "console-status-bar");
66 statusBarElement.appendChild(this._clearConsoleButton.element);
67 statusBarElement.appendChild(this._filterBar.filterButton().element);
68 statusBarElement.appendChild(this._executionContextSelector.element);
70 this._filtersContainer = this._contentsElement.createChild("div", "console-filters-header hidden");
71 this._filtersContainer.appendChild(this._filterBar.filtersElement());
72 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
73 this._filterBar.setName("consoleView");
74 this._filter.addFilters(this._filterBar);
76 this.messagesElement = document.createElement("div");
77 this.messagesElement.id = "console-messages";
78 this.messagesElement.className = "monospace";
79 this.messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
80 this._contentsElement.appendChild(this.messagesElement);
81 this._scrolledToBottom = true;
83 this.promptElement = document.createElement("div");
84 this.promptElement.id = "console-prompt";
85 this.promptElement.className = "source-code";
86 this.promptElement.spellcheck = false;
87 this.messagesElement.appendChild(this.promptElement);
88 this.messagesElement.appendChild(document.createElement("br"));
90 this.topGroup = new WebInspector.ConsoleGroup(null);
91 this.messagesElement.insertBefore(this.topGroup.element, this.promptElement);
92 this.currentGroup = this.topGroup;
94 this._registerShortcuts();
95 this.registerRequiredCSS("textPrompt.css");
97 this.messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
99 WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged.bind(this));
101 this._linkifier = new WebInspector.Linkifier();
103 /** @type {!Map.<!WebInspector.ConsoleMessage, !WebInspector.ConsoleViewMessage>} */
104 this._messageToViewMessage = new Map();
105 /** @type {!Array.<!WebInspector.ConsoleMessage>} */
106 this._consoleMessages = [];
108 this.prompt = new WebInspector.TextPromptWithHistory(this._completionsForTextPrompt.bind(this));
109 this.prompt.setSuggestBoxEnabled("generic-suggest");
110 this.prompt.renderAsBlock();
111 this.prompt.attach(this.promptElement);
112 this.prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
113 this.prompt.setHistoryData(WebInspector.settings.consoleHistory.get());
115 WebInspector.targetManager.targets().forEach(this._targetAdded, this);
116 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.TargetAdded, this._onTargetAdded, this);
118 this._filterStatusMessageElement = document.createElement("div");
119 this._filterStatusMessageElement.classList.add("console-message");
120 this._filterStatusTextElement = this._filterStatusMessageElement.createChild("span", "console-info");
121 this._filterStatusMessageElement.createTextChild(" ");
122 var resetFiltersLink = this._filterStatusMessageElement.createChild("span", "console-info node-link");
123 resetFiltersLink.textContent = WebInspector.UIString("Show all messages.");
124 resetFiltersLink.addEventListener("click", this._filter.reset.bind(this._filter), true);
126 this.messagesElement.insertBefore(this._filterStatusMessageElement, this.topGroup.element);
128 this._updateFilterStatus();
129 WebInspector.settings.consoleTimestampsEnabled.addChangeListener(this._consoleTimestampsSettingChanged, this);
132 WebInspector.ConsoleView.prototype = {
134 * @param {!WebInspector.Event} event
136 _onTargetAdded: function(event)
138 this._targetAdded(/**@type {!WebInspector.Target} */(event.data));
142 * @param {!WebInspector.Target} target
144 _targetAdded: function(target)
146 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded.bind(this, target), this);
147 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
148 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this);
149 target.consoleModel.messages.forEach(this._consoleMessageAdded.bind(this, target));
152 * @param {!WebInspector.ExecutionContextList} contextList
153 * @this {WebInspector.ConsoleView}
155 function loadContextList(contextList)
157 this._addExecutionContextList(target, contextList);
158 this._contextListChanged(target, contextList);
160 target.runtimeModel.contextLists().forEach(loadContextList, this);
161 target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextListAdded, this._executionContextListAdded.bind(this, target));
162 target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextListRemoved, this._executionContextListRemoved, this);
166 _consoleTimestampsSettingChanged: function(event)
168 var enabled = /** @type {boolean} */ (event.data);
169 this._messageToViewMessage.values().forEach(function(viewMessage) {
170 viewMessage.updateTimestamp(enabled);
177 defaultFocusedElement: function()
179 return this.promptElement
182 _onFiltersToggled: function(event)
184 var toggled = /** @type {boolean} */ (event.data);
185 this._filtersContainer.classList.toggle("hidden", !toggled);
189 * @param {!WebInspector.Event} event
191 _executionContextListAdded: function(target, event)
193 var contextList = /** @type {!WebInspector.ExecutionContextList} */ (event.data);
194 this._addExecutionContextList(target, contextList);
198 * @param {!WebInspector.ExecutionContextList} contextList
200 _addExecutionContextList: function(target, contextList)
203 var topLevelOption = this._executionContextSelector.createOption(contextList.displayName().trimMiddle(maxLength), contextList.url());
204 topLevelOption._executionContext = null;
205 topLevelOption._target = target;
206 this._topLevelOptionByContextListId[contextList.id()] = topLevelOption;
207 this._subOptionsByContextListId[contextList.id()] = [];
209 contextList.addEventListener(WebInspector.ExecutionContextList.EventTypes.Reset, this._contextListReset, this);
210 contextList.addEventListener(WebInspector.ExecutionContextList.EventTypes.ContextAdded, this._contextListChanged.bind(this, target, contextList));
214 * @param {!WebInspector.Event} event
216 _executionContextListRemoved: function(event)
218 var contextList = /** @type {!WebInspector.ExecutionContextList} */ (event.data);
220 this._removeSubOptions(contextList.id());
221 var topLevelOption = this._topLevelOptionByContextListId[contextList.id()];
222 this._executionContextSelector.removeOption(topLevelOption);
223 delete this._topLevelOptionByContextListId[contextList.id()];
224 delete this._subOptionsByContextListId[contextList.id()];
225 this._executionContextChanged();
229 * @param {string} contextListId
232 _removeSubOptions: function(contextListId)
234 var selectedOptionRemoved = false;
235 var subOptions = this._subOptionsByContextListId[contextListId];
236 for (var i = 0; i < subOptions.length; ++i) {
237 selectedOptionRemoved |= this._executionContextSelector.selectedOption() === subOptions[i];
238 this._executionContextSelector.removeOption(subOptions[i]);
240 this._subOptionsByContextListId[contextListId] = [];
241 return selectedOptionRemoved;
244 _executionContextChanged: function()
246 var runtimeModel = this._currentTarget().runtimeModel;
247 var runtimeContext = runtimeModel.currentExecutionContext();
248 if (this._currentExecutionContext() !== runtimeContext)
249 runtimeModel.setCurrentExecutionContext(this._currentExecutionContext());
251 this.prompt.clearAutoComplete(true);
255 * @return {?WebInspector.ExecutionContext}
257 _currentExecutionContext: function()
259 var option = this._executionContextSelector.selectedOption();
260 return option ? option._executionContext : null;
264 * @return {!WebInspector.Target}
266 _currentTarget: function()
268 var option = this._executionContextSelector.selectedOption();
269 return option ? option._target : WebInspector.targetManager.mainTarget();
273 * @param {!Element} proxyElement
274 * @param {!Range} wordRange
275 * @param {boolean} force
276 * @param {function(!Array.<string>, number=)} completionsReadyCallback
278 _completionsForTextPrompt: function(proxyElement, wordRange, force, completionsReadyCallback)
280 this._currentTarget().runtimeModel.completionsForTextPrompt(proxyElement, wordRange, force, completionsReadyCallback);
284 * @param {!WebInspector.Event} event
286 _contextListReset: function(event)
288 var contextList = /** @type {!WebInspector.ExecutionContextList} */ (event.data);
289 var option = this._topLevelOptionByContextListId[contextList.id()];
291 option.text = contextList.displayName().trimMiddle(maxLength);
292 option.title = contextList.url();
294 var selectedRemoved = this._removeSubOptions(contextList.id());
296 if (selectedRemoved) {
297 this._executionContextSelector.select(option);
298 this._executionContextChanged();
303 * @param {!WebInspector.ExecutionContextList} contextList
305 _contextListChanged: function(target, contextList)
307 var currentExecutionContext = this._currentExecutionContext();
308 var shouldSelectOption = this._removeSubOptions(contextList.id());
310 var topLevelOption = this._topLevelOptionByContextListId[contextList.id()];
311 var nextTopLevelOption = topLevelOption.nextSibling;
312 var subOptions = this._subOptionsByContextListId[contextList.id()];
313 var executionContexts = contextList.executionContexts();
314 for (var i = 0; i < executionContexts.length; ++i) {
315 if (executionContexts[i].isMainWorldContext) {
316 topLevelOption._executionContext = executionContexts[i];
319 var subOption = document.createElement("option");
320 subOption.text = "\u00a0\u00a0\u00a0\u00a0" + executionContexts[i].name;
321 subOption._executionContext = executionContexts[i];
322 subOption._target = target;
323 this._executionContextSelector.selectElement().insertBefore(subOption, nextTopLevelOption);
324 subOptions.push(subOption);
326 if (shouldSelectOption && executionContexts[i] === currentExecutionContext) {
327 this._executionContextSelector.select(subOption);
328 shouldSelectOption = false;
332 if (shouldSelectOption)
333 this._executionContextSelector.select(topLevelOption);
335 this._executionContextChanged();
340 this.prompt.hideSuggestBox();
341 this.prompt.clearAutoComplete(true);
346 if (!this.prompt.isCaretInsidePrompt())
347 this.prompt.moveCaretToEndOfPrompt();
352 if (this.promptElement === WebInspector.currentFocusElement())
354 WebInspector.setCurrentFocusElement(this.promptElement);
355 this.prompt.moveCaretToEndOfPrompt();
358 storeScrollPositions: function()
360 WebInspector.View.prototype.storeScrollPositions.call(this);
361 this._scrolledToBottom = this.messagesElement.isScrolledToBottom();
364 restoreScrollPositions: function()
366 if (this._scrolledToBottom)
367 this._immediatelyScrollIntoView();
369 WebInspector.View.prototype.restoreScrollPositions.call(this);
374 this.prompt.hideSuggestBox();
375 this.restoreScrollPositions();
378 _isScrollIntoViewScheduled: function()
380 return !!this._scrollIntoViewTimer;
383 _scheduleScrollIntoView: function()
385 if (this._scrollIntoViewTimer)
389 * @this {WebInspector.ConsoleView}
391 function scrollIntoView()
393 delete this._scrollIntoViewTimer;
394 this.messagesElement.scrollTop = this.messagesElement.scrollHeight;
396 this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 20);
399 _immediatelyScrollIntoView: function()
401 this.promptElement.scrollIntoView(true);
402 this._cancelScheduledScrollIntoView();
405 _cancelScheduledScrollIntoView: function()
407 if (!this._isScrollIntoViewScheduled())
410 clearTimeout(this._scrollIntoViewTimer);
411 delete this._scrollIntoViewTimer;
415 * @param {number=} count
417 _updateFilterStatus: function(count) {
418 count = (typeof count === "undefined") ? (this._consoleMessages.length - this._visibleViewMessages.length) : count;
419 this._filterStatusTextElement.textContent = WebInspector.UIString(count == 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", count);
420 this._filterStatusMessageElement.style.display = count ? "" : "none";
424 * @param {!WebInspector.ConsoleMessage} message
426 _consoleMessageAdded: function(target, message)
428 if (this._urlToMessageCount[message.url])
429 this._urlToMessageCount[message.url]++;
431 this._urlToMessageCount[message.url] = 1;
433 var previousMessage = this._consoleMessages.peekLast();
434 if (previousMessage && !message.isGroupMessage() && message.isEqual(previousMessage)) {
435 previousMessage.timestamp = message.timestamp;
436 this._messageToViewMessage.get(previousMessage).incrementRepeatCount();
440 this._consoleMessages.push(message);
441 var viewMessage = this._createViewMessage(target, message);
443 if (this._filter.shouldBeVisible(viewMessage))
444 this._showConsoleMessage(viewMessage);
446 this._updateFilterStatus();
450 * @param {!WebInspector.Event} event
452 _onConsoleMessageAdded: function(target, event)
454 var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data);
455 this._consoleMessageAdded(target, message);
459 * @param {!WebInspector.ConsoleViewMessage} viewMessage
461 _showConsoleMessage: function(viewMessage)
463 var message = viewMessage.consoleMessage();
465 // this.messagesElement.isScrolledToBottom() is forcing style recalculation.
466 // We just skip it if the scroll action has been scheduled.
467 if (!this._isScrollIntoViewScheduled() && ((viewMessage instanceof WebInspector.ConsoleCommandResult) || this.messagesElement.isScrolledToBottom()))
468 this._scheduleScrollIntoView();
470 this._visibleViewMessages.push(viewMessage);
472 if (message.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
473 var parentGroup = this.currentGroup.parentGroup;
475 this.currentGroup = parentGroup;
477 if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
478 var group = new WebInspector.ConsoleGroup(this.currentGroup);
479 this.currentGroup.messagesElement.appendChild(group.element);
480 this.currentGroup = group;
481 viewMessage.group = group;
483 this.currentGroup.addMessage(viewMessage);
486 if (this._searchRegex && viewMessage.matchesRegex(this._searchRegex)) {
487 this._searchResults.push(viewMessage);
488 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
493 * @param {!WebInspector.ConsoleMessage} message
494 * @return {!WebInspector.ConsoleViewMessage}
496 _createViewMessage: function(target, message)
498 var viewMessage = this._messageToViewMessage.get(message);
501 if (message.type === WebInspector.ConsoleMessage.MessageType.Command)
502 viewMessage = new WebInspector.ConsoleCommand(target, message);
504 viewMessage = new WebInspector.ConsoleViewMessage(target, message, this._linkifier);
505 this._messageToViewMessage.put(message, viewMessage);
509 _consoleCleared: function()
511 this._scrolledToBottom = true;
512 this._clearCurrentSearchResultHighlight();
513 this._updateFilterStatus(0);
515 for (var i = 0; i < this._visibleViewMessages.length; ++i)
516 this._visibleViewMessages[i].willHide();
518 this._visibleViewMessages = [];
519 this._searchResults = [];
520 this._messageToViewMessage.clear();
521 this._consoleMessages = [];
523 if (this._searchRegex)
524 this._searchableView.updateSearchMatchesCount(0);
526 this.currentGroup = this.topGroup;
527 this.topGroup.messagesElement.removeChildren();
529 this._linkifier.reset();
532 _handleContextMenuEvent: function(event)
534 if (event.target.enclosingNodeOrSelfWithNodeName("a"))
537 var contextMenu = new WebInspector.ContextMenu(event);
539 function monitoringXHRItemAction()
541 WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
543 contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction, WebInspector.settings.monitoringXHREnabled.get());
545 function preserveLogItemAction()
547 WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get());
549 contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction, WebInspector.settings.preserveConsoleLog.get());
551 var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message");
553 var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
555 if (sourceElement && sourceElement.message.url) {
556 var menuTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(sourceElement.message.url).displayName);
557 filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, sourceElement.message.url));
560 filterSubMenu.appendSeparator();
561 var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._filter.removeMessageURLFilter.bind(this._filter));
562 filterSubMenu.appendSeparator();
564 var hasFilters = false;
566 for (var url in this._filter.messageURLFilters) {
567 filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._filter.removeMessageURLFilter.bind(this._filter, url), true);
571 filterSubMenu.setEnabled(hasFilters || (sourceElement && sourceElement.message.url));
572 unhideAll.setEnabled(hasFilters);
574 contextMenu.appendSeparator();
575 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
577 var request = (sourceElement && sourceElement.message) ? sourceElement.message.request : null;
578 if (request && request.type === WebInspector.resourceTypes.XHR) {
579 contextMenu.appendSeparator();
580 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
586 _updateMessageList: function()
588 var group = this.topGroup;
589 var visibleMessageIndex = 0;
590 var newVisibleMessages = [];
592 if (this._searchRegex)
593 this._searchResults = [];
596 for (var i = 0; i < this._consoleMessages.length; ++i) {
597 var sourceMessage = this._consoleMessages[i];
598 var sourceViewMessage = this._messageToViewMessage.get(sourceMessage);
599 var visibleViewMessage = this._visibleViewMessages[visibleMessageIndex];
601 if (visibleViewMessage === sourceViewMessage) {
602 if (this._filter.shouldBeVisible(sourceViewMessage)) {
603 newVisibleMessages.push(this._visibleViewMessages[visibleMessageIndex]);
605 if (this._searchRegex && sourceViewMessage.matchesRegex(this._searchRegex))
606 this._searchResults.push(sourceViewMessage);
608 if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
609 anchor = group.element;
610 group = group.parentGroup || group;
611 } else if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroup || sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
612 group = sourceViewMessage.group;
613 anchor = group.messagesElement.firstChild;
615 anchor = sourceViewMessage.toMessageElement();
617 sourceViewMessage.willHide();
618 sourceViewMessage.toMessageElement().remove();
620 ++visibleMessageIndex;
622 if (this._filter.shouldBeVisible(sourceViewMessage)) {
624 if (this._searchRegex && sourceViewMessage.matchesRegex(this._searchRegex))
625 this._searchResults.push(sourceViewMessage);
627 group.addMessage(sourceViewMessage, anchor ? anchor.nextSibling : group.messagesElement.firstChild);
628 newVisibleMessages.push(sourceViewMessage);
629 anchor = sourceViewMessage.toMessageElement();
634 if (this._searchRegex)
635 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
637 this._visibleViewMessages = newVisibleMessages;
638 this._updateFilterStatus();
641 _monitoringXHREnabledSettingChanged: function(event)
643 ConsoleAgent.setMonitoringXHREnabled(event.data);
646 _messagesClicked: function()
648 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
649 this.prompt.moveCaretToEndOfPrompt();
652 _registerShortcuts: function()
654 this._shortcuts = {};
656 var shortcut = WebInspector.KeyboardShortcut;
657 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
659 var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
660 this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
661 var keys = [shortcutL];
662 if (WebInspector.isMac()) {
663 var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
664 this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
665 keys.unshift(shortcutK);
667 section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
669 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
670 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
673 shortcut.makeDescriptor(shortcut.Keys.Down),
674 shortcut.makeDescriptor(shortcut.Keys.Up)
676 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
678 if (WebInspector.isMac()) {
680 shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
681 shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
683 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
686 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
689 _requestClearMessages: function()
691 WebInspector.console.requestClearMessages();
694 _promptKeyDown: function(event)
696 if (isEnterKey(event)) {
697 this._enterKeyPressed(event);
701 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
702 var handler = this._shortcuts[shortcut];
705 event.preventDefault();
709 _enterKeyPressed: function(event)
711 if (event.altKey || event.ctrlKey || event.shiftKey)
716 this.prompt.clearAutoComplete(true);
718 var str = this.prompt.text;
721 this._appendCommand(str, true);
725 * @param {?WebInspector.RemoteObject} result
726 * @param {boolean} wasThrown
727 * @param {?WebInspector.ConsoleCommand} originatingCommand
729 _printResult: function(result, wasThrown, originatingCommand)
734 var target = result.target();
736 * @param {string=} url
737 * @param {number=} lineNumber
738 * @param {number=} columnNumber
739 * @this {WebInspector.ConsoleView}
741 function addMessage(url, lineNumber, columnNumber)
743 var resultMessage = new WebInspector.ConsoleCommandResult(/** @type {!WebInspector.RemoteObject} */ (result), wasThrown, originatingCommand, this._linkifier, url, lineNumber, columnNumber);
744 this._messageToViewMessage.put(resultMessage.consoleMessage(), resultMessage);
745 target.consoleModel.addMessage(resultMessage.consoleMessage());
748 if (result.type !== "function") {
749 addMessage.call(this);
753 target.debuggerAgent().getFunctionDetails(result.objectId, didGetDetails.bind(this));
756 * @param {?Protocol.Error} error
757 * @param {!DebuggerAgent.FunctionDetails} response
758 * @this {WebInspector.ConsoleView}
760 function didGetDetails(error, response)
763 console.error(error);
764 addMessage.call(this);
771 var script = WebInspector.debuggerModel.scriptForId(response.location.scriptId);
772 if (script && script.sourceURL) {
773 url = script.sourceURL;
774 lineNumber = response.location.lineNumber + 1;
775 columnNumber = response.location.columnNumber + 1;
777 addMessage.call(this, url, lineNumber, columnNumber);
782 * @param {string} text
783 * @param {boolean} useCommandLineAPI
785 _appendCommand: function(text, useCommandLineAPI)
787 this.prompt.text = "";
788 this._currentTarget().consoleModel.evaluateCommand(text, useCommandLineAPI);
792 * @param {!WebInspector.Event} event
794 _commandEvaluated: function(event)
796 var data = /**{{result: ?WebInspector.RemoteObject, wasThrown: boolean, text: string, commandMessage: !WebInspector.ConsoleMessage}} */ (event.data);
797 this.prompt.pushHistoryItem(data.text);
798 WebInspector.settings.consoleHistory.set(this.prompt.historyData.slice(-30));
799 this._printResult(data.result, data.wasThrown, /** @type {!WebInspector.ConsoleCommand} */ (this._messageToViewMessage.get(data.commandMessage)));
803 * @return {!Array.<!Element>}
805 elementsToRestoreScrollPositionsFor: function()
807 return [this.messagesElement];
810 searchCanceled: function()
812 this._clearCurrentSearchResultHighlight();
813 delete this._searchResults;
814 delete this._searchRegex;
818 * @param {string} query
819 * @param {boolean} shouldJump
821 performSearch: function(query, shouldJump)
823 this.searchCanceled();
824 this._searchableView.updateSearchMatchesCount(0);
825 this._searchRegex = createPlainTextSearchRegex(query, "gi");
827 this._searchResults = [];
828 for (var i = 0; i < this._visibleViewMessages.length; i++) {
829 if (this._visibleViewMessages[i].matchesRegex(this._searchRegex))
830 this._searchResults.push(this._visibleViewMessages[i]);
832 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
833 this._currentSearchResultIndex = -1;
834 if (shouldJump && this._searchResults.length)
835 this._jumpToSearchResult(0);
838 jumpToNextSearchResult: function()
840 if (!this._searchResults || !this._searchResults.length)
842 this._jumpToSearchResult((this._currentSearchResultIndex + 1) % this._searchResults.length);
845 jumpToPreviousSearchResult: function()
847 if (!this._searchResults || !this._searchResults.length)
849 var index = this._currentSearchResultIndex - 1;
851 index = this._searchResults.length - 1;
852 this._jumpToSearchResult(index);
855 _clearCurrentSearchResultHighlight: function()
857 if (!this._searchResults)
860 var highlightedViewMessage = this._searchResults[this._currentSearchResultIndex];
861 if (highlightedViewMessage)
862 highlightedViewMessage.clearHighlight();
863 this._currentSearchResultIndex = -1;
866 _jumpToSearchResult: function(index)
868 this._clearCurrentSearchResultHighlight();
869 this._currentSearchResultIndex = index;
870 this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex);
871 this._searchResults[index].highlightSearchResults(this._searchRegex);
874 __proto__: WebInspector.VBox.prototype
879 * @extends {WebInspector.Object}
880 * @param {!WebInspector.ConsoleView} view
882 WebInspector.ConsoleViewFilter = function(view)
885 this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
886 this._filterChanged = this.dispatchEventToListeners.bind(this, WebInspector.ConsoleViewFilter.Events.FilterChanged);
889 WebInspector.ConsoleViewFilter.Events = {
890 FilterChanged: "FilterChanged"
893 WebInspector.ConsoleViewFilter.prototype = {
894 addFilters: function(filterBar)
896 this._textFilterUI = new WebInspector.TextFilterUI(true);
897 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
898 filterBar.addFilter(this._textFilterUI);
901 {name: "error", label: WebInspector.UIString("Errors")},
902 {name: "warning", label: WebInspector.UIString("Warnings")},
903 {name: "info", label: WebInspector.UIString("Info")},
904 {name: "log", label: WebInspector.UIString("Logs")},
905 {name: "debug", label: WebInspector.UIString("Debug")}
907 this._levelFilterUI = new WebInspector.NamedBitSetFilterUI(levels, WebInspector.settings.messageLevelFilters);
908 this._levelFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
909 filterBar.addFilter(this._levelFilterUI);
912 _textFilterChanged: function(event)
914 this._filterRegex = this._textFilterUI.regex();
916 this._filterChanged();
920 * @param {string} url
922 addMessageURLFilter: function(url)
924 this._messageURLFilters[url] = true;
925 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
926 this._filterChanged();
930 * @param {string} url
932 removeMessageURLFilter: function(url)
935 this._messageURLFilters = {};
937 delete this._messageURLFilters[url];
939 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
940 this._filterChanged();
946 get messageURLFilters()
948 return this._messageURLFilters;
952 * @param {!WebInspector.ConsoleViewMessage|undefined} viewMessage
955 shouldBeVisible: function(viewMessage)
959 var message = viewMessage.consoleMessage();
960 if ((message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed || message.type === WebInspector.ConsoleMessage.MessageType.EndGroup))
963 if (message.type === WebInspector.ConsoleMessage.MessageType.Result || message.type === WebInspector.ConsoleMessage.MessageType.Command)
966 if (message.url && this._messageURLFilters[message.url])
969 if (message.level && !this._levelFilterUI.accept(message.level))
972 if (this._filterRegex) {
973 this._filterRegex.lastIndex = 0;
974 if (!viewMessage.matchesRegex(this._filterRegex))
983 this._messageURLFilters = {};
984 WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
985 WebInspector.settings.messageLevelFilters.set({});
986 this._filterChanged();
989 __proto__: WebInspector.Object.prototype
995 * @extends {WebInspector.ConsoleViewMessage}
996 * @param {!WebInspector.ConsoleMessage} message
998 WebInspector.ConsoleCommand = function(target, message)
1000 WebInspector.ConsoleViewMessage.call(this, target, message, null);
1003 WebInspector.ConsoleCommand.prototype = {
1004 wasShown: function()
1008 willHide: function()
1012 clearHighlight: function()
1014 var highlightedMessage = this._formattedCommand;
1015 delete this._formattedCommand;
1016 this._formatCommand();
1017 this._element.replaceChild(this._formattedCommand, highlightedMessage);
1021 * @param {!RegExp} regexObject
1023 highlightSearchResults: function(regexObject)
1025 regexObject.lastIndex = 0;
1026 var match = regexObject.exec(this.text);
1027 var matchRanges = [];
1029 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
1030 match = regexObject.exec(this.text);
1032 WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
1033 this._element.scrollIntoViewIfNeeded();
1037 * @param {!RegExp} regexObject
1040 matchesRegex: function(regexObject)
1042 regexObject.lastIndex = 0;
1043 return regexObject.test(this.text);
1047 * @return {!Element}
1049 toMessageElement: function()
1051 if (!this._element) {
1052 this._element = document.createElement("div");
1053 this._element.command = this;
1054 this._element.className = "console-user-command";
1056 this._formatCommand();
1057 this._element.appendChild(this._formattedCommand);
1059 return this._element;
1062 _formatCommand: function()
1064 this._formattedCommand = document.createElement("span");
1065 this._formattedCommand.className = "console-message-text source-code";
1066 this._formattedCommand.textContent = this.text;
1069 __proto__: WebInspector.ConsoleViewMessage.prototype
1073 * @extends {WebInspector.ConsoleViewMessage}
1075 * @param {!WebInspector.RemoteObject} result
1076 * @param {boolean} wasThrown
1077 * @param {?WebInspector.ConsoleCommand} originatingCommand
1078 * @param {!WebInspector.Linkifier} linkifier
1079 * @param {string=} url
1080 * @param {number=} lineNumber
1081 * @param {number=} columnNumber
1083 WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingCommand, linkifier, url, lineNumber, columnNumber)
1085 this.originatingCommand = originatingCommand;
1086 var level = wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
1088 var message = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, url, lineNumber, columnNumber, undefined, [result]);
1089 WebInspector.ConsoleViewMessage.call(this, result.target(), message, linkifier);
1092 WebInspector.ConsoleCommandResult.prototype = {
1095 * @param {!WebInspector.RemoteObject} array
1098 useArrayPreviewInFormatter: function(array)
1104 * @return {!Element}
1106 toMessageElement: function()
1108 var element = WebInspector.ConsoleViewMessage.prototype.toMessageElement.call(this);
1109 element.classList.add("console-user-command-result");
1113 __proto__: WebInspector.ConsoleViewMessage.prototype
1119 WebInspector.ConsoleGroup = function(parentGroup)
1121 this.parentGroup = parentGroup;
1123 var element = document.createElement("div");
1124 element.className = "console-group";
1125 element.group = this;
1126 this.element = element;
1129 var bracketElement = document.createElement("div");
1130 bracketElement.className = "console-group-bracket";
1131 element.appendChild(bracketElement);
1134 var messagesElement = document.createElement("div");
1135 messagesElement.className = "console-group-messages";
1136 element.appendChild(messagesElement);
1137 this.messagesElement = messagesElement;
1140 WebInspector.ConsoleGroup.prototype = {
1142 * @param {!WebInspector.ConsoleViewMessage} viewMessage
1143 * @param {!Node=} node
1145 addMessage: function(viewMessage, node)
1147 var message = viewMessage.consoleMessage();
1148 var element = viewMessage.toMessageElement();
1150 if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
1151 this.messagesElement.parentNode.insertBefore(element, this.messagesElement);
1152 element.addEventListener("click", this._titleClicked.bind(this), false);
1153 var groupElement = element.enclosingNodeOrSelfWithClass("console-group");
1154 if (groupElement && message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
1155 groupElement.classList.add("collapsed");
1157 this.messagesElement.insertBefore(element, node || null);
1158 viewMessage.wasShown();
1161 if (element.previousSibling && viewMessage.originatingCommand && element.previousSibling.command === viewMessage.originatingCommand)
1162 element.previousSibling.classList.add("console-adjacent-user-command-result");
1165 _titleClicked: function(event)
1167 var groupTitleElement = event.target.enclosingNodeOrSelfWithClass("console-group-title");
1168 if (groupTitleElement) {
1169 var groupElement = groupTitleElement.enclosingNodeOrSelfWithClass("console-group");
1170 if (groupElement && !groupElement.classList.toggle("collapsed")) {
1171 if (groupElement.group) {
1172 groupElement.group.wasShown();
1175 groupTitleElement.scrollIntoViewIfNeeded(true);
1177 event.consume(true);
1180 wasShown: function()
1182 if (this.element.classList.contains("collapsed"))
1184 var node = this.messagesElement.firstChild;
1186 if (node.classList.contains("console-message") && node.message)
1187 node.message.wasShown();
1188 if (node.classList.contains("console-group") && node.group)
1189 node.group.wasShown();
1190 node = node.nextSibling;
1197 * @implements {WebInspector.ActionDelegate}
1199 WebInspector.ConsoleView.ShowConsoleActionDelegate = function()
1203 WebInspector.ConsoleView.ShowConsoleActionDelegate.prototype = {
1207 handleAction: function()
1209 WebInspector.console.show();