2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * @extends {WebInspector.VBox}
33 * @implements {WebInspector.Searchable}
34 * @implements {WebInspector.TargetManager.Observer}
35 * @implements {WebInspector.ViewportControl.Provider}
37 WebInspector
.ConsoleView = function()
39 WebInspector
.VBox
.call(this);
40 this.setMinimumSize(0, 35);
41 this.registerRequiredCSS("ui/filter.css");
42 this.registerRequiredCSS("console/consoleView.css");
44 this._searchableView
= new WebInspector
.SearchableView(this);
45 this._searchableView
.setPlaceholder(WebInspector
.UIString("Find string in logs"));
46 this._searchableView
.setMinimalSearchQuerySize(0);
47 this._searchableView
.show(this.element
);
49 this._contentsElement
= this._searchableView
.element
;
50 this._contentsElement
.classList
.add("console-view");
51 /** @type {!Array.<!WebInspector.ConsoleViewMessage>} */
52 this._visibleViewMessages
= [];
53 this._urlToMessageCount
= {};
54 this._hiddenByFilterCount
= 0;
57 * @type {!Array.<!WebInspector.ConsoleView.RegexMatchRange>}
59 this._regexMatchRanges
= [];
61 this._clearConsoleButton
= WebInspector
.ToolbarButton
.createActionButton("console.clear");
63 this._executionContextComboBox
= new WebInspector
.ToolbarComboBox(null, "console-context");
64 this._executionContextComboBox
.setMaxWidth(200);
65 this._executionContextModel
= new WebInspector
.ExecutionContextModel(this._executionContextComboBox
.selectElement());
67 this._filter
= new WebInspector
.ConsoleViewFilter(this);
68 this._filter
.addEventListener(WebInspector
.ConsoleViewFilter
.Events
.FilterChanged
, this._updateMessageList
.bind(this));
70 this._filterBar
= new WebInspector
.FilterBar("consoleView");
72 this._preserveLogCheckbox
= new WebInspector
.ToolbarCheckbox(WebInspector
.UIString("Preserve log"), WebInspector
.UIString("Do not clear log on page reload / navigation"), WebInspector
.moduleSetting("preserveConsoleLog"));
73 this._progressToolbarItem
= new WebInspector
.ToolbarItem(createElement("div"));
75 var toolbar
= new WebInspector
.Toolbar(this._contentsElement
);
76 toolbar
.appendToolbarItem(this._clearConsoleButton
);
77 toolbar
.appendToolbarItem(this._filterBar
.filterButton());
78 toolbar
.appendToolbarItem(this._executionContextComboBox
);
79 toolbar
.appendToolbarItem(this._preserveLogCheckbox
);
80 toolbar
.appendToolbarItem(this._progressToolbarItem
);
82 this._contentsElement
.appendChild(this._filterBar
.filtersElement());
83 this._filter
.addFilters(this._filterBar
);
85 this._viewport
= new WebInspector
.ViewportControl(this);
86 this._viewport
.setStickToBottom(true);
87 this._viewport
.contentElement().classList
.add("console-group", "console-group-messages");
88 this._contentsElement
.appendChild(this._viewport
.element
);
89 this._messagesElement
= this._viewport
.element
;
90 this._messagesElement
.id
= "console-messages";
91 this._messagesElement
.classList
.add("monospace");
92 this._messagesElement
.addEventListener("click", this._messagesClicked
.bind(this), true);
94 this._viewportThrottler
= new WebInspector
.Throttler(50);
96 this._filterStatusMessageElement
= createElementWithClass("div", "console-message");
97 this._messagesElement
.insertBefore(this._filterStatusMessageElement
, this._messagesElement
.firstChild
);
98 this._filterStatusTextElement
= this._filterStatusMessageElement
.createChild("span", "console-info");
99 this._filterStatusMessageElement
.createTextChild(" ");
100 var resetFiltersLink
= this._filterStatusMessageElement
.createChild("span", "console-info link");
101 resetFiltersLink
.textContent
= WebInspector
.UIString("Show all messages.");
102 resetFiltersLink
.addEventListener("click", this._filter
.reset
.bind(this._filter
), true);
104 this._topGroup
= WebInspector
.ConsoleGroup
.createTopGroup();
105 this._currentGroup
= this._topGroup
;
107 this._promptElement
= this._messagesElement
.createChild("div", "source-code");
108 this._promptElement
.id
= "console-prompt";
109 this._promptElement
.spellcheck
= false;
111 this._searchableView
.setDefaultFocusedElement(this._promptElement
);
113 // FIXME: This is a workaround for the selection machinery bug. See crbug.com/410899
114 var selectAllFixer
= this._messagesElement
.createChild("div", "console-view-fix-select-all");
115 selectAllFixer
.textContent
= ".";
117 this._showAllMessagesCheckbox
= new WebInspector
.ToolbarCheckbox(WebInspector
.UIString("Show all messages"));
118 this._showAllMessagesCheckbox
.inputElement
.checked
= true;
119 this._showAllMessagesCheckbox
.inputElement
.addEventListener("change", this._updateMessageList
.bind(this), false);
121 this._showAllMessagesCheckbox
.element
.classList
.add("hidden");
123 toolbar
.appendToolbarItem(this._showAllMessagesCheckbox
);
125 this._registerShortcuts();
127 this._messagesElement
.addEventListener("contextmenu", this._handleContextMenuEvent
.bind(this), false);
128 WebInspector
.moduleSetting("monitoringXHREnabled").addChangeListener(this._monitoringXHREnabledSettingChanged
, this);
130 this._linkifier
= new WebInspector
.Linkifier();
132 /** @type {!Array.<!WebInspector.ConsoleViewMessage>} */
133 this._consoleMessages
= [];
134 this._viewMessageSymbol
= Symbol("viewMessage");
136 this._prompt
= new WebInspector
.TextPromptWithHistory(WebInspector
.ExecutionContextSelector
.completionsForTextPromptInCurrentContext
);
137 this._prompt
.setSuggestBoxEnabled(true);
138 this._prompt
.setAutocompletionTimeout(0);
139 this._prompt
.renderAsBlock();
140 var proxyElement
= this._prompt
.attach(this._promptElement
);
141 proxyElement
.addEventListener("keydown", this._promptKeyDown
.bind(this), false);
143 this._consoleHistorySetting
= WebInspector
.settings
.createLocalSetting("consoleHistory", []);
144 var historyData
= this._consoleHistorySetting
.get();
145 this._prompt
.setHistoryData(historyData
);
147 this._updateFilterStatus();
148 WebInspector
.moduleSetting("consoleTimestampsEnabled").addChangeListener(this._consoleTimestampsSettingChanged
, this);
150 this._registerWithMessageSink();
151 WebInspector
.targetManager
.observeTargets(this);
152 WebInspector
.targetManager
.addEventListener(WebInspector
.TargetManager
.Events
.MainFrameNavigated
, this._onMainFrameNavigated
, this);
154 this._initConsoleMessages();
156 WebInspector
.context
.addFlavorChangeListener(WebInspector
.ExecutionContext
, this._executionContextChanged
, this);
159 WebInspector
.ConsoleView
.persistedHistorySize
= 300;
161 WebInspector
.ConsoleView
.prototype = {
163 * @return {!WebInspector.SearchableView}
165 searchableView: function()
167 return this._searchableView
;
171 * @param {!WebInspector.Event} event
173 _onMainFrameNavigated: function(event
)
175 var frame
= /** @type {!WebInspector.ResourceTreeFrame} */(event
.data
);
176 WebInspector
.console
.addMessage(WebInspector
.UIString("Navigated to %s", frame
.url
));
179 _initConsoleMessages: function()
181 var mainTarget
= WebInspector
.targetManager
.mainTarget();
182 if (!mainTarget
|| !mainTarget
.resourceTreeModel
.cachedResourcesLoaded()) {
183 WebInspector
.targetManager
.addModelListener(WebInspector
.ResourceTreeModel
, WebInspector
.ResourceTreeModel
.EventTypes
.CachedResourcesLoaded
, this._onResourceTreeModelLoaded
, this);
186 this._fetchMultitargetMessages();
190 * @param {!WebInspector.Event} event
192 _onResourceTreeModelLoaded: function(event
)
194 var resourceTreeModel
= event
.target
;
195 if (resourceTreeModel
.target() !== WebInspector
.targetManager
.mainTarget())
197 WebInspector
.targetManager
.removeModelListener(WebInspector
.ResourceTreeModel
, WebInspector
.ResourceTreeModel
.EventTypes
.CachedResourcesLoaded
, this._onResourceTreeModelLoaded
, this);
198 this._fetchMultitargetMessages();
201 _fetchMultitargetMessages: function()
203 WebInspector
.multitargetConsoleModel
.addEventListener(WebInspector
.ConsoleModel
.Events
.ConsoleCleared
, this._consoleCleared
, this);
204 WebInspector
.multitargetConsoleModel
.addEventListener(WebInspector
.ConsoleModel
.Events
.MessageAdded
, this._onConsoleMessageAdded
, this);
205 WebInspector
.multitargetConsoleModel
.addEventListener(WebInspector
.ConsoleModel
.Events
.MessageUpdated
, this._onConsoleMessageUpdated
, this);
206 WebInspector
.multitargetConsoleModel
.addEventListener(WebInspector
.ConsoleModel
.Events
.CommandEvaluated
, this._commandEvaluated
, this);
207 WebInspector
.multitargetConsoleModel
.messages().forEach(this._addConsoleMessage
, this);
214 itemCount: function()
216 return this._visibleViewMessages
.length
;
221 * @param {number} index
222 * @return {?WebInspector.ViewportElement}
224 itemElement: function(index
)
226 return this._visibleViewMessages
[index
];
231 * @param {number} index
234 fastHeight: function(index
)
236 return this._visibleViewMessages
[index
].fastHeight();
243 minimumRowHeight: function()
250 * @param {!WebInspector.Target} target
252 targetAdded: function(target
)
254 this._viewport
.invalidate();
255 if (WebInspector
.targetManager
.targets().length
> 1 && WebInspector
.targetManager
.mainTarget().isPage())
256 this._showAllMessagesCheckbox
.element
.classList
.toggle("hidden", false);
261 * @param {!WebInspector.Target} target
263 targetRemoved: function(target
)
267 _registerWithMessageSink: function()
269 WebInspector
.console
.messages().forEach(this._addSinkMessage
, this);
270 WebInspector
.console
.addEventListener(WebInspector
.Console
.Events
.MessageAdded
, messageAdded
, this);
273 * @param {!WebInspector.Event} event
274 * @this {WebInspector.ConsoleView}
276 function messageAdded(event
)
278 this._addSinkMessage(/** @type {!WebInspector.Console.Message} */ (event
.data
));
283 * @param {!WebInspector.Console.Message} message
285 _addSinkMessage: function(message
)
287 var level
= WebInspector
.ConsoleMessage
.MessageLevel
.Debug
;
288 switch (message
.level
) {
289 case WebInspector
.Console
.MessageLevel
.Error
:
290 level
= WebInspector
.ConsoleMessage
.MessageLevel
.Error
;
292 case WebInspector
.Console
.MessageLevel
.Warning
:
293 level
= WebInspector
.ConsoleMessage
.MessageLevel
.Warning
;
297 var consoleMessage
= new WebInspector
.ConsoleMessage(null, WebInspector
.ConsoleMessage
.MessageSource
.Other
, level
, message
.text
,
298 undefined, undefined, undefined, undefined, undefined, undefined, undefined, message
.timestamp
);
299 this._addConsoleMessage(consoleMessage
);
303 * @param {!WebInspector.Event} event
305 _consoleTimestampsSettingChanged: function(event
)
307 var enabled
= /** @type {boolean} */ (event
.data
);
308 this._updateMessageList();
309 this._consoleMessages
.forEach(function(viewMessage
) {
310 viewMessage
.updateTimestamp(enabled
);
318 defaultFocusedElement: function()
320 return this._promptElement
;
323 _executionContextChanged: function()
325 this._prompt
.clearAutoComplete(true);
326 if (!this._showAllMessagesCheckbox
.checked())
327 this._updateMessageList();
332 this._hidePromptSuggestBox();
337 this._viewport
.refresh();
338 if (!this._prompt
.isCaretInsidePrompt())
339 this._prompt
.moveCaretToEndOfPrompt();
344 if (this._promptElement
=== WebInspector
.currentFocusElement())
346 WebInspector
.setCurrentFocusElement(this._promptElement
);
347 this._prompt
.moveCaretToEndOfPrompt();
350 restoreScrollPositions: function()
352 if (this._viewport
.scrolledToBottom())
353 this._immediatelyScrollToBottom();
355 WebInspector
.Widget
.prototype.restoreScrollPositions
.call(this);
360 this._scheduleViewportRefresh();
361 this._hidePromptSuggestBox();
362 if (this._viewport
.scrolledToBottom())
363 this._immediatelyScrollToBottom();
366 _hidePromptSuggestBox: function()
368 this._prompt
.hideSuggestBox();
369 this._prompt
.clearAutoComplete(true);
372 _scheduleViewportRefresh: function()
375 * @this {WebInspector.ConsoleView}
376 * @return {!Promise.<undefined>}
378 function invalidateViewport()
380 if (this._needsFullUpdate
) {
381 this._updateMessageList();
382 delete this._needsFullUpdate
;
384 this._viewport
.invalidate();
386 return Promise
.resolve();
388 this._viewportThrottler
.schedule(invalidateViewport
.bind(this));
391 _immediatelyScrollToBottom: function()
393 // This will scroll viewport and trigger its refresh.
394 this._promptElement
.scrollIntoView(true);
397 _updateFilterStatus: function()
399 this._filterStatusTextElement
.textContent
= WebInspector
.UIString(this._hiddenByFilterCount
=== 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", this._hiddenByFilterCount
);
400 this._filterStatusMessageElement
.style
.display
= this._hiddenByFilterCount
? "" : "none";
404 * @param {!WebInspector.Event} event
406 _onConsoleMessageAdded: function(event
)
408 var message
= /** @type {!WebInspector.ConsoleMessage} */ (event
.data
);
409 this._addConsoleMessage(message
);
413 * @param {!WebInspector.ConsoleMessage} message
415 _addConsoleMessage: function(message
)
418 * @param {!WebInspector.ConsoleViewMessage} viewMessage1
419 * @param {!WebInspector.ConsoleViewMessage} viewMessage2
422 function compareTimestamps(viewMessage1
, viewMessage2
)
424 return WebInspector
.ConsoleMessage
.timestampComparator(viewMessage1
.consoleMessage(), viewMessage2
.consoleMessage());
427 if (message
.type
=== WebInspector
.ConsoleMessage
.MessageType
.Command
|| message
.type
=== WebInspector
.ConsoleMessage
.MessageType
.Result
)
428 message
.timestamp
= this._consoleMessages
.length
? this._consoleMessages
.peekLast().consoleMessage().timestamp
: 0;
429 var viewMessage
= this._createViewMessage(message
);
430 message
[this._viewMessageSymbol
] = viewMessage
;
431 var insertAt
= insertionIndexForObjectInListSortedByFunction(viewMessage
, this._consoleMessages
, compareTimestamps
, true);
432 var insertedInMiddle
= insertAt
< this._consoleMessages
.length
;
433 this._consoleMessages
.splice(insertAt
, 0, viewMessage
);
435 if (this._urlToMessageCount
[message
.url
])
436 ++this._urlToMessageCount
[message
.url
];
438 this._urlToMessageCount
[message
.url
] = 1;
440 if (!insertedInMiddle
) {
441 this._appendMessageToEnd(viewMessage
);
442 this._updateFilterStatus();
443 this._searchableView
.updateSearchMatchesCount(this._regexMatchRanges
.length
);
445 this._needsFullUpdate
= true;
448 this._scheduleViewportRefresh();
449 this._consoleMessageAddedForTest(viewMessage
);
453 * @param {!WebInspector.Event} event
455 _onConsoleMessageUpdated: function(event
)
457 var message
= /** @type {!WebInspector.ConsoleMessage} */ (event
.data
);
458 var viewMessage
= message
[this._viewMessageSymbol
];
460 viewMessage
.updateMessageElement();
461 this._updateMessageList();
466 * @param {!WebInspector.ConsoleViewMessage} viewMessage
468 _consoleMessageAddedForTest: function(viewMessage
) { },
471 * @param {!WebInspector.ConsoleViewMessage} viewMessage
473 _appendMessageToEnd: function(viewMessage
)
475 if (!this._filter
.shouldBeVisible(viewMessage
)) {
476 this._hiddenByFilterCount
++;
480 if (this._tryToCollapseMessages(viewMessage
, this._visibleViewMessages
.peekLast()))
483 var lastMessage
= this._visibleViewMessages
.peekLast();
484 if (viewMessage
.consoleMessage().type
=== WebInspector
.ConsoleMessage
.MessageType
.EndGroup
) {
485 if (lastMessage
&& !this._currentGroup
.messagesHidden())
486 lastMessage
.incrementCloseGroupDecorationCount();
487 this._currentGroup
= this._currentGroup
.parentGroup();
490 if (!this._currentGroup
.messagesHidden()) {
491 var originatingMessage
= viewMessage
.consoleMessage().originatingMessage();
492 if (lastMessage
&& originatingMessage
&& lastMessage
.consoleMessage() === originatingMessage
)
493 lastMessage
.toMessageElement().classList
.add("console-adjacent-user-command-result");
495 this._visibleViewMessages
.push(viewMessage
);
496 this._searchMessage(this._visibleViewMessages
.length
- 1);
499 if (viewMessage
.consoleMessage().isGroupStartMessage())
500 this._currentGroup
= new WebInspector
.ConsoleGroup(this._currentGroup
, viewMessage
);
502 this._messageAppendedForTests();
505 _messageAppendedForTests: function()
507 // This method is sniffed in tests.
511 * @param {!WebInspector.ConsoleMessage} message
512 * @return {!WebInspector.ConsoleViewMessage}
514 _createViewMessage: function(message
)
516 var nestingLevel
= this._currentGroup
.nestingLevel();
517 switch (message
.type
) {
518 case WebInspector
.ConsoleMessage
.MessageType
.Command
:
519 return new WebInspector
.ConsoleCommand(message
, this._linkifier
, nestingLevel
);
520 case WebInspector
.ConsoleMessage
.MessageType
.Result
:
521 return new WebInspector
.ConsoleCommandResult(message
, this._linkifier
, nestingLevel
);
522 case WebInspector
.ConsoleMessage
.MessageType
.StartGroupCollapsed
:
523 case WebInspector
.ConsoleMessage
.MessageType
.StartGroup
:
524 return new WebInspector
.ConsoleGroupViewMessage(message
, this._linkifier
, nestingLevel
);
526 return new WebInspector
.ConsoleViewMessage(message
, this._linkifier
, nestingLevel
);
530 _consoleCleared: function()
532 this._currentMatchRangeIndex
= -1;
533 this._consoleMessages
= [];
534 this._updateMessageList();
535 this._hidePromptSuggestBox();
536 this._linkifier
.reset();
539 _handleContextMenuEvent: function(event
)
541 if (event
.target
.enclosingNodeOrSelfWithNodeName("a"))
544 var contextMenu
= new WebInspector
.ContextMenu(event
);
545 if (event
.target
.isSelfOrDescendant(this._promptElement
)) {
550 function monitoringXHRItemAction()
552 WebInspector
.moduleSetting("monitoringXHREnabled").set(!WebInspector
.moduleSetting("monitoringXHREnabled").get());
554 contextMenu
.appendCheckboxItem(WebInspector
.UIString("Log XMLHttpRequests"), monitoringXHRItemAction
, WebInspector
.moduleSetting("monitoringXHREnabled").get());
556 var sourceElement
= event
.target
.enclosingNodeOrSelfWithClass("console-message-wrapper");
557 var consoleMessage
= sourceElement
? sourceElement
.message
.consoleMessage() : null;
559 var filterSubMenu
= contextMenu
.appendSubMenuItem(WebInspector
.UIString("Filter"));
561 if (consoleMessage
&& consoleMessage
.url
) {
562 var menuTitle
= WebInspector
.UIString
.capitalize("Hide ^messages from %s", new WebInspector
.ParsedURL(consoleMessage
.url
).displayName
);
563 filterSubMenu
.appendItem(menuTitle
, this._filter
.addMessageURLFilter
.bind(this._filter
, consoleMessage
.url
));
566 filterSubMenu
.appendSeparator();
567 var unhideAll
= filterSubMenu
.appendItem(WebInspector
.UIString
.capitalize("Unhide ^all"), this._filter
.removeMessageURLFilter
.bind(this._filter
));
568 filterSubMenu
.appendSeparator();
570 var hasFilters
= false;
572 for (var url
in this._filter
.messageURLFilters
) {
573 filterSubMenu
.appendCheckboxItem(String
.sprintf("%s (%d)", new WebInspector
.ParsedURL(url
).displayName
, this._urlToMessageCount
[url
]), this._filter
.removeMessageURLFilter
.bind(this._filter
, url
), true);
577 filterSubMenu
.setEnabled(hasFilters
|| (consoleMessage
&& consoleMessage
.url
));
578 unhideAll
.setEnabled(hasFilters
);
580 contextMenu
.appendSeparator();
581 contextMenu
.appendAction("console.clear");
582 contextMenu
.appendItem(WebInspector
.UIString("Save as..."), this._saveConsole
.bind(this));
584 var request
= consoleMessage
? consoleMessage
.request
: null;
585 if (request
&& request
.resourceType() === WebInspector
.resourceTypes
.XHR
) {
586 contextMenu
.appendSeparator();
587 contextMenu
.appendItem(WebInspector
.UIString("Replay XHR"), request
.replayXHR
.bind(request
));
593 _saveConsole: function()
595 var filename
= String
.sprintf("%s-%d.log", WebInspector
.targetManager
.inspectedPageDomain(), Date
.now());
596 var stream
= new WebInspector
.FileOutputStream();
598 var progressIndicator
= new WebInspector
.ProgressIndicator();
599 progressIndicator
.setTitle(WebInspector
.UIString("Writing file…"));
600 progressIndicator
.setTotalWork(this.itemCount());
604 var messageIndex
= 0;
606 stream
.open(filename
, openCallback
.bind(this));
609 * @param {boolean} accepted
610 * @this {WebInspector.ConsoleView}
612 function openCallback(accepted
)
616 this._progressToolbarItem
.element
.appendChild(progressIndicator
.element
);
617 writeNextChunk
.call(this, stream
);
621 * @param {!WebInspector.OutputStream} stream
622 * @param {string=} error
623 * @this {WebInspector.ConsoleView}
625 function writeNextChunk(stream
, error
)
627 if (messageIndex
>= this.itemCount() || error
) {
629 progressIndicator
.done();
633 for (var i
= 0; i
< chunkSize
&& i
+ messageIndex
< this.itemCount(); ++i
) {
634 var message
= this.itemElement(messageIndex
+ i
);
635 lines
.push(message
.searchableElement().deepTextContent());
638 stream
.write(lines
.join("\n") + "\n", writeNextChunk
.bind(this));
639 progressIndicator
.setWorked(messageIndex
);
645 * @param {!WebInspector.ConsoleViewMessage} lastMessage
646 * @param {?WebInspector.ConsoleViewMessage=} viewMessage
649 _tryToCollapseMessages: function(lastMessage
, viewMessage
)
651 if (!WebInspector
.moduleSetting("consoleTimestampsEnabled").get() && viewMessage
&& !lastMessage
.consoleMessage().isGroupMessage() && lastMessage
.consoleMessage().isEqual(viewMessage
.consoleMessage())) {
652 viewMessage
.incrementRepeatCount();
659 _updateMessageList: function()
661 this._topGroup
= WebInspector
.ConsoleGroup
.createTopGroup();
662 this._currentGroup
= this._topGroup
;
663 this._regexMatchRanges
= [];
664 this._hiddenByFilterCount
= 0;
665 for (var i
= 0; i
< this._visibleViewMessages
.length
; ++i
) {
666 this._visibleViewMessages
[i
].resetCloseGroupDecorationCount();
667 this._visibleViewMessages
[i
].resetIncrementRepeatCount();
669 this._visibleViewMessages
= [];
670 for (var i
= 0; i
< this._consoleMessages
.length
; ++i
)
671 this._appendMessageToEnd(this._consoleMessages
[i
]);
672 this._updateFilterStatus();
673 this._searchableView
.updateSearchMatchesCount(this._regexMatchRanges
.length
);
674 this._viewport
.invalidate();
678 * @param {!WebInspector.Event} event
680 _monitoringXHREnabledSettingChanged: function(event
)
682 var enabled
= /** @type {boolean} */ (event
.data
);
683 WebInspector
.targetManager
.targets().forEach(function(target
) {target
.networkAgent().setMonitoringXHREnabled(enabled
);});
687 * @param {!Event} event
689 _messagesClicked: function(event
)
691 var targetElement
= event
.deepElementFromPoint();
692 if (!this._prompt
.isCaretInsidePrompt() && (!targetElement
|| targetElement
.isComponentSelectionCollapsed()))
693 this._prompt
.moveCaretToEndOfPrompt();
694 var groupMessage
= event
.target
.enclosingNodeOrSelfWithClass("console-group-title");
697 var consoleGroupViewMessage
= groupMessage
.parentElement
.message
;
698 consoleGroupViewMessage
.setCollapsed(!consoleGroupViewMessage
.collapsed());
699 this._updateMessageList();
702 _registerShortcuts: function()
704 this._shortcuts
= {};
706 var shortcut
= WebInspector
.KeyboardShortcut
;
707 var section
= WebInspector
.shortcutsScreen
.section(WebInspector
.UIString("Console"));
709 var shortcutL
= shortcut
.makeDescriptor("l", WebInspector
.KeyboardShortcut
.Modifiers
.Ctrl
);
710 var keys
= [shortcutL
];
711 if (WebInspector
.isMac()) {
712 var shortcutK
= shortcut
.makeDescriptor("k", WebInspector
.KeyboardShortcut
.Modifiers
.Meta
);
713 keys
.unshift(shortcutK
);
715 section
.addAlternateKeys(keys
, WebInspector
.UIString("Clear console"));
717 section
.addKey(shortcut
.makeDescriptor(shortcut
.Keys
.Tab
), WebInspector
.UIString("Autocomplete common prefix"));
718 section
.addKey(shortcut
.makeDescriptor(shortcut
.Keys
.Right
), WebInspector
.UIString("Accept suggestion"));
720 var shortcutU
= shortcut
.makeDescriptor("u", WebInspector
.KeyboardShortcut
.Modifiers
.Ctrl
);
721 this._shortcuts
[shortcutU
.key
] = this._clearPromptBackwards
.bind(this);
722 section
.addAlternateKeys([shortcutU
], WebInspector
.UIString("Clear console prompt"));
725 shortcut
.makeDescriptor(shortcut
.Keys
.Down
),
726 shortcut
.makeDescriptor(shortcut
.Keys
.Up
)
728 section
.addRelatedKeys(keys
, WebInspector
.UIString("Next/previous line"));
730 if (WebInspector
.isMac()) {
732 shortcut
.makeDescriptor("N", shortcut
.Modifiers
.Alt
),
733 shortcut
.makeDescriptor("P", shortcut
.Modifiers
.Alt
)
735 section
.addRelatedKeys(keys
, WebInspector
.UIString("Next/previous command"));
738 section
.addKey(shortcut
.makeDescriptor(shortcut
.Keys
.Enter
), WebInspector
.UIString("Execute command"));
741 _clearPromptBackwards: function()
743 this._prompt
.setText("");
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
780 * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
782 _printResult: function(result
, wasThrown
, originatingConsoleMessage
, exceptionDetails
)
787 var level
= wasThrown
? WebInspector
.ConsoleMessage
.MessageLevel
.Error
: WebInspector
.ConsoleMessage
.MessageLevel
.Log
;
790 message
= new WebInspector
.ConsoleMessage(result
.target(), WebInspector
.ConsoleMessage
.MessageSource
.JS
, level
, "", WebInspector
.ConsoleMessage
.MessageType
.Result
, undefined, undefined, undefined, undefined, [result
]);
792 message
= new WebInspector
.ConsoleMessage(result
.target(), WebInspector
.ConsoleMessage
.MessageSource
.JS
, level
, exceptionDetails
.text
, WebInspector
.ConsoleMessage
.MessageType
.Result
, exceptionDetails
.url
, exceptionDetails
.line
, exceptionDetails
.column
, undefined, [WebInspector
.UIString("Uncaught"), result
], exceptionDetails
.stackTrace
, undefined, undefined, undefined, exceptionDetails
.scriptId
);
793 message
.setOriginatingMessage(originatingConsoleMessage
);
794 result
.target().consoleModel
.addMessage(message
);
798 * @param {string} text
799 * @param {boolean} useCommandLineAPI
801 _appendCommand: function(text
, useCommandLineAPI
)
803 this._prompt
.setText("");
804 var currentExecutionContext
= WebInspector
.context
.flavor(WebInspector
.ExecutionContext
);
805 if (currentExecutionContext
) {
806 WebInspector
.ConsoleModel
.evaluateCommandInConsole(currentExecutionContext
, text
, useCommandLineAPI
);
807 if (WebInspector
.inspectorView
.currentPanel() && WebInspector
.inspectorView
.currentPanel().name
=== "console")
808 WebInspector
.userMetrics
.CommandEvaluatedInConsolePanel
.record();
813 * @param {!WebInspector.Event} event
815 _commandEvaluated: function(event
)
817 var data
= /**{{result: ?WebInspector.RemoteObject, wasThrown: boolean, text: string, commandMessage: !WebInspector.ConsoleMessage}} */ (event
.data
);
818 this._prompt
.pushHistoryItem(data
.text
);
819 this._consoleHistorySetting
.set(this._prompt
.historyData().slice(-WebInspector
.ConsoleView
.persistedHistorySize
));
820 this._printResult(data
.result
, data
.wasThrown
, data
.commandMessage
, data
.exceptionDetails
);
825 * @return {!Array.<!Element>}
827 elementsToRestoreScrollPositionsFor: function()
829 return [this._messagesElement
];
835 searchCanceled: function()
837 this._cleanupAfterSearch();
838 for (var i
= 0; i
< this._visibleViewMessages
.length
; ++i
) {
839 var message
= this._visibleViewMessages
[i
];
840 message
.setSearchRegex(null);
842 this._currentMatchRangeIndex
= -1;
843 this._regexMatchRanges
= [];
844 delete this._searchRegex
;
845 this._viewport
.refresh();
850 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
851 * @param {boolean} shouldJump
852 * @param {boolean=} jumpBackwards
854 performSearch: function(searchConfig
, shouldJump
, jumpBackwards
)
856 this.searchCanceled();
857 this._searchableView
.updateSearchMatchesCount(0);
859 this._searchRegex
= searchConfig
.toSearchRegex(true);
861 this._regexMatchRanges
= [];
862 this._currentMatchRangeIndex
= -1;
865 this._searchShouldJumpBackwards
= !!jumpBackwards
;
867 this._searchProgressIndicator
= new WebInspector
.ProgressIndicator();
868 this._searchProgressIndicator
.setTitle(WebInspector
.UIString("Searching…"));
869 this._searchProgressIndicator
.setTotalWork(this._visibleViewMessages
.length
);
870 this._progressToolbarItem
.element
.appendChild(this._searchProgressIndicator
.element
);
872 this._innerSearch(0);
875 _cleanupAfterSearch: function()
877 delete this._searchShouldJumpBackwards
;
878 if (this._innerSearchTimeoutId
) {
879 clearTimeout(this._innerSearchTimeoutId
);
880 delete this._innerSearchTimeoutId
;
882 if (this._searchProgressIndicator
) {
883 this._searchProgressIndicator
.done();
884 delete this._searchProgressIndicator
;
888 _searchFinishedForTests: function()
890 // This method is sniffed in tests.
894 * @param {number} index
896 _innerSearch: function(index
)
898 delete this._innerSearchTimeoutId
;
899 if (this._searchProgressIndicator
.isCanceled()) {
900 this._cleanupAfterSearch();
904 var startTime
= Date
.now();
905 for (; index
< this._visibleViewMessages
.length
&& Date
.now() - startTime
< 100; ++index
)
906 this._searchMessage(index
);
908 this._searchableView
.updateSearchMatchesCount(this._regexMatchRanges
.length
);
909 if (typeof this._searchShouldJumpBackwards
!== "undefined" && this._regexMatchRanges
.length
) {
910 this._jumpToMatch(this._searchShouldJumpBackwards
? -1 : 0);
911 delete this._searchShouldJumpBackwards
;
914 if (index
=== this._visibleViewMessages
.length
) {
915 this._cleanupAfterSearch();
916 setTimeout(this._searchFinishedForTests
.bind(this), 0);
920 this._innerSearchTimeoutId
= setTimeout(this._innerSearch
.bind(this, index
), 100);
921 this._searchProgressIndicator
.setWorked(index
);
925 * @param {number} index
927 _searchMessage: function(index
)
929 var message
= this._visibleViewMessages
[index
];
930 message
.setSearchRegex(this._searchRegex
);
931 for (var i
= 0; i
< message
.searchCount(); ++i
) {
932 this._regexMatchRanges
.push({
942 jumpToNextSearchResult: function()
944 this._jumpToMatch(this._currentMatchRangeIndex
+ 1);
950 jumpToPreviousSearchResult: function()
952 this._jumpToMatch(this._currentMatchRangeIndex
- 1);
959 supportsCaseSensitiveSearch: function()
968 supportsRegexSearch: function()
974 * @param {number} index
976 _jumpToMatch: function(index
)
978 if (!this._regexMatchRanges
.length
)
981 var currentSearchResultClassName
= "current-search-result";
984 if (this._currentMatchRangeIndex
>= 0) {
985 matchRange
= this._regexMatchRanges
[this._currentMatchRangeIndex
];
986 var message
= this._visibleViewMessages
[matchRange
.messageIndex
];
987 message
.searchHighlightNode(matchRange
.matchIndex
).classList
.remove(currentSearchResultClassName
);
990 index
= mod(index
, this._regexMatchRanges
.length
);
991 this._currentMatchRangeIndex
= index
;
992 this._searchableView
.updateCurrentMatchIndex(index
);
993 matchRange
= this._regexMatchRanges
[index
];
994 var message
= this._visibleViewMessages
[matchRange
.messageIndex
];
995 var highlightNode
= message
.searchHighlightNode(matchRange
.matchIndex
);
996 highlightNode
.classList
.add(currentSearchResultClassName
);
997 this._viewport
.scrollItemIntoView(matchRange
.messageIndex
);
998 highlightNode
.scrollIntoViewIfNeeded();
1001 __proto__
: WebInspector
.VBox
.prototype
1006 * @extends {WebInspector.Object}
1007 * @param {!WebInspector.ConsoleView} view
1009 WebInspector
.ConsoleViewFilter = function(view
)
1011 this._messageURLFiltersSetting
= WebInspector
.settings
.createSetting("messageURLFilters", {});
1012 this._messageLevelFiltersSetting
= WebInspector
.settings
.createSetting("messageLevelFilters", {});
1015 this._messageURLFilters
= this._messageURLFiltersSetting
.get();
1016 this._filterChanged
= this.dispatchEventToListeners
.bind(this, WebInspector
.ConsoleViewFilter
.Events
.FilterChanged
);
1019 WebInspector
.ConsoleViewFilter
.Events
= {
1020 FilterChanged
: "FilterChanged"
1023 WebInspector
.ConsoleViewFilter
.prototype = {
1024 addFilters: function(filterBar
)
1026 this._textFilterUI
= new WebInspector
.TextFilterUI(true);
1027 this._textFilterUI
.addEventListener(WebInspector
.FilterUI
.Events
.FilterChanged
, this._textFilterChanged
, this);
1028 filterBar
.addFilter(this._textFilterUI
);
1030 this._hideNetworkMessagesCheckbox
= new WebInspector
.CheckboxFilterUI("hide-network-messages", WebInspector
.UIString("Hide network messages"), true, WebInspector
.moduleSetting("hideNetworkMessages"));
1031 this._hideNetworkMessagesCheckbox
.addEventListener(WebInspector
.FilterUI
.Events
.FilterChanged
, this._filterChanged
.bind(this), this);
1032 filterBar
.addFilter(this._hideNetworkMessagesCheckbox
);
1035 {name
: WebInspector
.ConsoleMessage
.MessageLevel
.Error
, label
: WebInspector
.UIString("Errors")},
1036 {name
: WebInspector
.ConsoleMessage
.MessageLevel
.Warning
, label
: WebInspector
.UIString("Warnings")},
1037 {name
: WebInspector
.ConsoleMessage
.MessageLevel
.Info
, label
: WebInspector
.UIString("Info")},
1038 {name
: WebInspector
.ConsoleMessage
.MessageLevel
.Log
, label
: WebInspector
.UIString("Logs")},
1039 {name
: WebInspector
.ConsoleMessage
.MessageLevel
.Debug
, label
: WebInspector
.UIString("Debug")},
1040 {name
: WebInspector
.ConsoleMessage
.MessageLevel
.RevokedError
, label
: WebInspector
.UIString("Handled")}
1042 this._levelFilterUI
= new WebInspector
.NamedBitSetFilterUI(levels
, this._messageLevelFiltersSetting
);
1043 this._levelFilterUI
.addEventListener(WebInspector
.FilterUI
.Events
.FilterChanged
, this._filterChanged
, this);
1044 filterBar
.addFilter(this._levelFilterUI
);
1047 _textFilterChanged: function(event
)
1049 this._filterRegex
= this._textFilterUI
.regex();
1051 this._filterChanged();
1055 * @param {string} url
1057 addMessageURLFilter: function(url
)
1059 this._messageURLFilters
[url
] = true;
1060 this._messageURLFiltersSetting
.set(this._messageURLFilters
);
1061 this._filterChanged();
1065 * @param {string} url
1067 removeMessageURLFilter: function(url
)
1070 this._messageURLFilters
= {};
1072 delete this._messageURLFilters
[url
];
1074 this._messageURLFiltersSetting
.set(this._messageURLFilters
);
1075 this._filterChanged();
1079 * @returns {!Object}
1081 get messageURLFilters()
1083 return this._messageURLFilters
;
1087 * @param {!WebInspector.ConsoleViewMessage} viewMessage
1090 shouldBeVisible: function(viewMessage
)
1092 var message
= viewMessage
.consoleMessage();
1093 var executionContext
= WebInspector
.context
.flavor(WebInspector
.ExecutionContext
);
1094 if (!message
.target())
1097 if (!this._view
._showAllMessagesCheckbox
.checked() && executionContext
) {
1098 if (message
.target() !== executionContext
.target())
1100 if (message
.executionContextId
&& message
.executionContextId
!== executionContext
.id
) {
1105 if (WebInspector
.moduleSetting("hideNetworkMessages").get() && viewMessage
.consoleMessage().source
=== WebInspector
.ConsoleMessage
.MessageSource
.Network
)
1108 if (viewMessage
.consoleMessage().isGroupMessage())
1111 if (message
.type
=== WebInspector
.ConsoleMessage
.MessageType
.Result
|| message
.type
=== WebInspector
.ConsoleMessage
.MessageType
.Command
)
1114 if (message
.url
&& this._messageURLFilters
[message
.url
])
1117 if (message
.level
&& !this._levelFilterUI
.accept(message
.level
))
1120 if (this._filterRegex
) {
1121 this._filterRegex
.lastIndex
= 0;
1122 if (!viewMessage
.matchesFilterRegex(this._filterRegex
))
1131 this._messageURLFilters
= {};
1132 this._messageURLFiltersSetting
.set(this._messageURLFilters
);
1133 this._messageLevelFiltersSetting
.set({});
1134 this._view
._showAllMessagesCheckbox
.inputElement
.checked
= true;
1135 WebInspector
.moduleSetting("hideNetworkMessages").set(false);
1136 this._textFilterUI
.setValue("");
1137 this._filterChanged();
1140 __proto__
: WebInspector
.Object
.prototype
1146 * @extends {WebInspector.ConsoleViewMessage}
1147 * @param {!WebInspector.ConsoleMessage} message
1148 * @param {!WebInspector.Linkifier} linkifier
1149 * @param {number} nestingLevel
1151 WebInspector
.ConsoleCommand = function(message
, linkifier
, nestingLevel
)
1153 WebInspector
.ConsoleViewMessage
.call(this, message
, linkifier
, nestingLevel
);
1156 WebInspector
.ConsoleCommand
.prototype = {
1159 * @return {!Element})
1161 searchableElement: function()
1163 return this.contentElement();
1168 * @return {!Element}
1170 contentElement: function()
1172 if (!this._element
) {
1173 this._element
= createElementWithClass("div", "console-user-command");
1174 this._element
.message
= this;
1176 this._formattedCommand
= createElementWithClass("span", "console-message-text source-code");
1177 this._formattedCommand
.textContent
= this.text
;
1178 this._element
.appendChild(this._formattedCommand
);
1180 var javascriptSyntaxHighlighter
= new WebInspector
.DOMSyntaxHighlighter("text/javascript", true);
1181 javascriptSyntaxHighlighter
.syntaxHighlightNode(this._formattedCommand
).then(this._updateSearch
.bind(this))
1183 return this._element
;
1186 _updateSearch: function()
1188 this.setSearchRegex(this.searchRegex());
1191 __proto__
: WebInspector
.ConsoleViewMessage
.prototype
1196 * @extends {WebInspector.ConsoleViewMessage}
1197 * @param {!WebInspector.ConsoleMessage} message
1198 * @param {!WebInspector.Linkifier} linkifier
1199 * @param {number} nestingLevel
1201 WebInspector
.ConsoleCommandResult = function(message
, linkifier
, nestingLevel
)
1203 WebInspector
.ConsoleViewMessage
.call(this, message
, linkifier
, nestingLevel
);
1206 WebInspector
.ConsoleCommandResult
.prototype = {
1209 * @param {!WebInspector.RemoteObject} array
1212 useArrayPreviewInFormatter: function(array
)
1219 * @return {!Element}
1221 contentElement: function()
1223 var element
= WebInspector
.ConsoleViewMessage
.prototype.contentElement
.call(this);
1224 element
.classList
.add("console-user-command-result");
1225 this.updateTimestamp(false);
1229 __proto__
: WebInspector
.ConsoleViewMessage
.prototype
1234 * @param {?WebInspector.ConsoleGroup} parentGroup
1235 * @param {?WebInspector.ConsoleViewMessage} groupMessage
1237 WebInspector
.ConsoleGroup = function(parentGroup
, groupMessage
)
1239 this._parentGroup
= parentGroup
;
1240 this._nestingLevel
= parentGroup
? parentGroup
.nestingLevel() + 1 : 0;
1241 this._messagesHidden
= groupMessage
&& groupMessage
.collapsed() || this._parentGroup
&& this._parentGroup
.messagesHidden();
1245 * @return {!WebInspector.ConsoleGroup}
1247 WebInspector
.ConsoleGroup
.createTopGroup = function()
1249 return new WebInspector
.ConsoleGroup(null, null);
1252 WebInspector
.ConsoleGroup
.prototype = {
1256 messagesHidden: function()
1258 return this._messagesHidden
;
1264 nestingLevel: function()
1266 return this._nestingLevel
;
1270 * @return {?WebInspector.ConsoleGroup}
1272 parentGroup: function()
1274 return this._parentGroup
|| this;
1280 * @implements {WebInspector.ActionDelegate}
1282 WebInspector
.ConsoleView
.ActionDelegate = function()
1286 WebInspector
.ConsoleView
.ActionDelegate
.prototype = {
1289 * @param {!WebInspector.Context} context
1290 * @param {string} actionId
1292 handleAction: function(context
, actionId
)
1294 if (actionId
=== "console.show")
1295 WebInspector
.console
.show();
1296 else if (actionId
=== "console.clear")
1297 WebInspector
.ConsoleModel
.clearConsole();
1302 * @typedef {{messageIndex: number, matchIndex: number}}
1304 WebInspector
.ConsoleView
.RegexMatchRange
;