1 /* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2009 Joseph Pecoraro 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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. 17 * 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. 28 */ 29 30 /** 31 * @extends {WebInspector.View} 32 * @implements {WebInspector.Searchable} 33 * @constructor 34 * @param {boolean} hideContextSelector 35 */ 36 WebInspector.ConsoleView = function(hideContextSelector) 37 { 38 WebInspector.View.call(this); 39 this.registerRequiredCSS("filter.css"); 40 41 this._searchableView = new WebInspector.SearchableView(this); 42 this._searchableView.setMinimalSearchQuerySize(0); 43 this._searchableView.show(this.element); 44 45 this._contentsElement = this._searchableView.element; 46 this._contentsElement.classList.add("fill", "vbox", "console-view"); 47 this._visibleMessagesIndices = []; 48 this._urlToMessageCount = {}; 49 50 this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item"); 51 this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this); 52 53 this._frameSelector = new WebInspector.StatusBarComboBox(this._frameChanged.bind(this), "console-context"); 54 this._contextSelector = new WebInspector.StatusBarComboBox(this._contextChanged.bind(this), "console-context"); 55 56 this._filter = new WebInspector.ConsoleViewFilter(); 57 this._filter.addEventListener(WebInspector.ConsoleViewFilter.Events.FilterChanged, this._updateMessageList.bind(this)); 58 59 if (hideContextSelector) { 60 this._frameSelector.element.classList.add("hidden"); 61 this._contextSelector.element.classList.add("hidden"); 62 } 63 64 this._filterBar = new WebInspector.FilterBar(); 65 66 var statusBarElement = this._contentsElement.createChild("div", "console-status-bar"); 67 statusBarElement.appendChild(this._clearConsoleButton.element); 68 statusBarElement.appendChild(this._filterBar.filterButton().element); 69 statusBarElement.appendChild(this._frameSelector.element); 70 statusBarElement.appendChild(this._contextSelector.element); 71 72 this._filtersContainer = this._contentsElement.createChild("div", "console-filters-header hidden"); 73 this._filtersContainer.appendChild(this._filterBar.filtersElement()); 74 this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this); 75 this._filter.addFilters(this._filterBar); 76 77 this.messagesElement = document.createElement("div"); 78 this.messagesElement.id = "console-messages"; 79 this.messagesElement.className = "monospace"; 80 this.messagesElement.addEventListener("click", this._messagesClicked.bind(this), true); 81 this._contentsElement.appendChild(this.messagesElement); 82 this._scrolledToBottom = true; 83 84 this.promptElement = document.createElement("div"); 85 this.promptElement.id = "console-prompt"; 86 this.promptElement.className = "source-code"; 87 this.promptElement.spellcheck = false; 88 this.messagesElement.appendChild(this.promptElement); 89 this.messagesElement.appendChild(document.createElement("br")); 90 91 this.topGroup = new WebInspector.ConsoleGroup(null); 92 this.messagesElement.insertBefore(this.topGroup.element, this.promptElement); 93 this.currentGroup = this.topGroup; 94 95 this._registerShortcuts(); 96 this.registerRequiredCSS("textPrompt.css"); 97 98 this.messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false); 99 100 WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged.bind(this)); 101 102 WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this); 103 WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); 104 105 this._linkifier = new WebInspector.Linkifier(); 106 107 this.prompt = new WebInspector.TextPromptWithHistory(WebInspector.runtimeModel.completionsForTextPrompt.bind(WebInspector.runtimeModel)); 108 this.prompt.setSuggestBoxEnabled("generic-suggest"); 109 this.prompt.renderAsBlock(); 110 this.prompt.attach(this.promptElement); 111 this.prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false); 112 this.prompt.setHistoryData(WebInspector.settings.consoleHistory.get()); 113 114 WebInspector.runtimeModel.contextLists().forEach(this._addFrame, this); 115 WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, this._frameAdded, this); 116 WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, this._frameRemoved, this); 117 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); 125 126 this.messagesElement.insertBefore(this._filterStatusMessageElement, this.topGroup.element); 127 128 this._updateFilterStatus(); 129 } 130 131 WebInspector.ConsoleView.prototype = { 132 /** 133 * @return {!Element} 134 */ 135 defaultFocusedElement: function() 136 { 137 return this.promptElement 138 }, 139 140 _onFiltersToggled: function(event) 141 { 142 var toggled = /** @type {boolean} */ (event.data); 143 this._filtersContainer.enableStyleClass("hidden", !toggled); 144 }, 145 146 /** 147 * @param {!WebInspector.Event} event 148 */ 149 _frameAdded: function(event) 150 { 151 var contextList = /** @type {!WebInspector.FrameExecutionContextList} */ (event.data); 152 this._addFrame(contextList); 153 }, 154 155 /** 156 * @param {!WebInspector.FrameExecutionContextList} contextList 157 */ 158 _addFrame: function(contextList) 159 { 160 var option = this._frameSelector.createOption(contextList.displayName, contextList.url); 161 option._contextList = contextList; 162 contextList._consoleOption = option; 163 contextList.addEventListener(WebInspector.FrameExecutionContextList.EventTypes.ContextsUpdated, this._frameUpdated, this); 164 contextList.addEventListener(WebInspector.FrameExecutionContextList.EventTypes.ContextAdded, this._contextAdded, this); 165 this._frameChanged(); 166 }, 167 168 /** 169 * @param {!WebInspector.Event} event 170 */ 171 _frameRemoved: function(event) 172 { 173 var contextList = /** @type {!WebInspector.FrameExecutionContextList} */ (event.data); 174 this._frameSelector.removeOption(contextList._consoleOption); 175 this._frameChanged(); 176 }, 177 178 _frameChanged: function() 179 { 180 var context = this._currentFrame(); 181 if (!context) { 182 WebInspector.runtimeModel.setCurrentExecutionContext(null); 183 this._contextSelector.element.classList.add("hidden"); 184 return; 185 } 186 187 var executionContexts = context.executionContexts(); 188 if (executionContexts.length) 189 WebInspector.runtimeModel.setCurrentExecutionContext(executionContexts[0]); 190 191 if (executionContexts.length === 1) { 192 this._contextSelector.element.classList.add("hidden"); 193 return; 194 } 195 this._contextSelector.element.classList.remove("hidden"); 196 this._contextSelector.removeOptions(); 197 for (var i = 0; i < executionContexts.length; ++i) 198 this._appendContextOption(executionContexts[i]); 199 }, 200 201 /** 202 * @param {!WebInspector.ExecutionContext} executionContext 203 */ 204 _appendContextOption: function(executionContext) 205 { 206 if (!WebInspector.runtimeModel.currentExecutionContext()) 207 WebInspector.runtimeModel.setCurrentExecutionContext(executionContext); 208 var option = this._contextSelector.createOption(executionContext.name, executionContext.id); 209 option._executionContext = executionContext; 210 }, 211 212 _contextChanged: function() 213 { 214 var option = this._contextSelector.selectedOption(); 215 WebInspector.runtimeModel.setCurrentExecutionContext(option ? option._executionContext : null); 216 }, 217 218 /** 219 * @param {!WebInspector.Event} event 220 */ 221 _frameUpdated: function(event) 222 { 223 var contextList = /** @type {!WebInspector.FrameExecutionContextList} */ (event.data); 224 var option = contextList._consoleOption; 225 option.text = contextList.displayName; 226 option.title = contextList.url; 227 }, 228 229 /** 230 * @param {!WebInspector.Event} event 231 */ 232 _contextAdded: function(event) 233 { 234 var contextList = /** @type {!WebInspector.FrameExecutionContextList} */ (event.data); 235 if (contextList === this._currentFrame()) 236 this._frameChanged(); 237 }, 238 239 /** 240 * @return {!WebInspector.FrameExecutionContextList|undefined} 241 */ 242 _currentFrame: function() 243 { 244 var option = this._frameSelector.selectedOption(); 245 return option ? option._contextList : undefined; 246 }, 247 248 willHide: function() 249 { 250 this.prompt.hideSuggestBox(); 251 this.prompt.clearAutoComplete(true); 252 }, 253 254 wasShown: function() 255 { 256 if (!this.prompt.isCaretInsidePrompt()) 257 this.prompt.moveCaretToEndOfPrompt(); 258 }, 259 260 afterShow: function() 261 { 262 WebInspector.setCurrentFocusElement(this.promptElement); 263 }, 264 265 storeScrollPositions: function() 266 { 267 WebInspector.View.prototype.storeScrollPositions.call(this); 268 this._scrolledToBottom = this.messagesElement.isScrolledToBottom(); 269 }, 270 271 restoreScrollPositions: function() 272 { 273 if (this._scrolledToBottom) 274 this._immediatelyScrollIntoView(); 275 else 276 WebInspector.View.prototype.restoreScrollPositions.call(this); 277 }, 278 279 onResize: function() 280 { 281 this.restoreScrollPositions(); 282 }, 283 284 _isScrollIntoViewScheduled: function() 285 { 286 return !!this._scrollIntoViewTimer; 287 }, 288 289 _scheduleScrollIntoView: function() 290 { 291 if (this._scrollIntoViewTimer) 292 return; 293 294 /** 295 * @this {WebInspector.ConsoleView} 296 */ 297 function scrollIntoView() 298 { 299 delete this._scrollIntoViewTimer; 300 this.messagesElement.scrollTop = this.messagesElement.scrollHeight - this.messagesElement.clientHeight; 301 } 302 this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 20); 303 }, 304 305 _immediatelyScrollIntoView: function() 306 { 307 this.promptElement.scrollIntoView(true); 308 this._cancelScheduledScrollIntoView(); 309 }, 310 311 _cancelScheduledScrollIntoView: function() 312 { 313 if (!this._isScrollIntoViewScheduled()) 314 return; 315 316 clearTimeout(this._scrollIntoViewTimer); 317 delete this._scrollIntoViewTimer; 318 }, 319 320 /** 321 * @param {number=} count 322 */ 323 _updateFilterStatus: function(count) { 324 count = (typeof count === "undefined") ? (WebInspector.console.messages.length - this._visibleMessagesIndices.length) : count; 325 this._filterStatusTextElement.textContent = WebInspector.UIString(count == 1 ? "%d message is hidden by filters." : "%d messages are hidden by filters.", count); 326 this._filterStatusMessageElement.style.display = count ? "" : "none"; 327 }, 328 329 /** 330 * @param {!WebInspector.Event} event 331 */ 332 _consoleMessageAdded: function(event) 333 { 334 var message = /** @type {!WebInspector.ConsoleMessage} */ (event.data); 335 var index = message.index; 336 337 if (this._urlToMessageCount[message.url]) 338 this._urlToMessageCount[message.url]++; 339 else 340 this._urlToMessageCount[message.url] = 1; 341 342 if (this._filter.shouldBeVisible(message)) 343 this._showConsoleMessage(index); 344 else 345 this._updateFilterStatus(); 346 }, 347 348 _showConsoleMessage: function(index) 349 { 350 var message = WebInspector.console.messages[index]; 351 352 // this.messagesElement.isScrolledToBottom() is forcing style recalculation. 353 // We just skip it if the scroll action has been scheduled. 354 if (!this._isScrollIntoViewScheduled() && ((message instanceof WebInspector.ConsoleCommandResult) || this.messagesElement.isScrolledToBottom())) 355 this._scheduleScrollIntoView(); 356 357 this._visibleMessagesIndices.push(index); 358 359 if (message.type === WebInspector.ConsoleMessage.MessageType.EndGroup) { 360 var parentGroup = this.currentGroup.parentGroup; 361 if (parentGroup) 362 this.currentGroup = parentGroup; 363 } else { 364 if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) { 365 var group = new WebInspector.ConsoleGroup(this.currentGroup); 366 this.currentGroup.messagesElement.appendChild(group.element); 367 this.currentGroup = group; 368 message.group = group; 369 } 370 this.currentGroup.addMessage(message); 371 } 372 373 if (this._searchRegex && message.matchesRegex(this._searchRegex)) { 374 this._searchResultsIndices.push(index); 375 this._searchableView.updateSearchMatchesCount(this._searchResultsIndices.length); 376 } 377 }, 378 379 _consoleCleared: function() 380 { 381 this._scrolledToBottom = true; 382 for (var i = 0; i < this._visibleMessagesIndices.length; ++i) 383 WebInspector.console.messages[this._visibleMessagesIndices[i]].willHide(); 384 this._visibleMessagesIndices = []; 385 this._searchResultsIndices = []; 386 387 if (this._searchRegex) 388 this._searchableView.updateSearchMatchesCount(0); 389 390 this.currentGroup = this.topGroup; 391 this.topGroup.messagesElement.removeChildren(); 392 393 this._clearCurrentSearchResultHighlight(); 394 this._updateFilterStatus(0); 395 396 this._linkifier.reset(); 397 }, 398 399 _handleContextMenuEvent: function(event) 400 { 401 if (!window.getSelection().isCollapsed) { 402 // If there is a selection, we want to show our normal context menu 403 // (with Copy, etc.), and not Clear Console. 404 return; 405 } 406 407 if (event.target.enclosingNodeOrSelfWithNodeName("a")) 408 return; 409 410 var contextMenu = new WebInspector.ContextMenu(event); 411 412 function monitoringXHRItemAction() 413 { 414 WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get()); 415 } 416 contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction.bind(this), WebInspector.settings.monitoringXHREnabled.get()); 417 418 function preserveLogItemAction() 419 { 420 WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get()); 421 } 422 contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction.bind(this), WebInspector.settings.preserveConsoleLog.get()); 423 424 var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message"); 425 426 var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter")); 427 428 if (sourceElement && sourceElement.message.url) { 429 var menuTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(sourceElement.message.url).displayName); 430 filterSubMenu.appendItem(menuTitle, this._filter.addMessageURLFilter.bind(this._filter, sourceElement.message.url)); 431 } 432 433 filterSubMenu.appendSeparator(); 434 var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._filter.removeMessageURLFilter.bind(this._filter)); 435 filterSubMenu.appendSeparator(); 436 437 var hasFilters = false; 438 439 for (var url in this._filter.messageURLFilters) { 440 filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._filter.removeMessageURLFilter.bind(this._filter, url), true); 441 hasFilters = true; 442 } 443 444 filterSubMenu.setEnabled(hasFilters || (sourceElement && sourceElement.message.url)); 445 unhideAll.setEnabled(hasFilters); 446 447 contextMenu.appendSeparator(); 448 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this)); 449 450 var request = (sourceElement && sourceElement.message) ? sourceElement.message.request() : null; 451 if (request && request.type === WebInspector.resourceTypes.XHR) { 452 contextMenu.appendSeparator(); 453 contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId)); 454 } 455 456 contextMenu.show(); 457 }, 458 459 _updateMessageList: function() 460 { 461 var group = this.topGroup; 462 var sourceMessages = WebInspector.console.messages; 463 var visibleMessageIndex = 0; 464 var newVisibleMessages = []; 465 466 if (this._searchRegex) 467 this._searchResultsIndices = []; 468 469 var anchor = null; 470 for (var i = 0; i < sourceMessages.length; ++i) { 471 var sourceMessage = sourceMessages[i]; 472 var visibleMessage = WebInspector.console.messages[this._visibleMessagesIndices[visibleMessageIndex]]; 473 474 if (visibleMessage === sourceMessage) { 475 if (this._filter.shouldBeVisible(visibleMessage)) { 476 newVisibleMessages.push(this._visibleMessagesIndices[visibleMessageIndex]); 477 478 if (this._searchRegex && sourceMessage.matchesRegex(this._searchRegex)) 479 this._searchResultsIndices.push(i); 480 481 if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.EndGroup) { 482 anchor = group.element; 483 group = group.parentGroup || group; 484 } else if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroup || sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) { 485 group = sourceMessage.group; 486 anchor = group.messagesElement.firstChild; 487 } else 488 anchor = visibleMessage.toMessageElement(); 489 } else { 490 visibleMessage.willHide(); 491 visibleMessage.toMessageElement().remove(); 492 } 493 ++visibleMessageIndex; 494 } else { 495 if (this._filter.shouldBeVisible(sourceMessage)) { 496 497 if (this._searchRegex && sourceMessage.matchesRegex(this._searchRegex)) 498 this._searchResultsIndices.push(i); 499 500 group.addMessage(sourceMessage, anchor ? anchor.nextSibling : group.messagesElement.firstChild); 501 newVisibleMessages.push(i); 502 anchor = sourceMessage.toMessageElement(); 503 } 504 } 505 } 506 507 if (this._searchRegex) 508 this._searchableView.updateSearchMatchesCount(this._searchResultsIndices.length); 509 510 this._visibleMessagesIndices = newVisibleMessages; 511 this._updateFilterStatus(); 512 }, 513 514 _monitoringXHREnabledSettingChanged: function(event) 515 { 516 ConsoleAgent.setMonitoringXHREnabled(event.data); 517 }, 518 519 _messagesClicked: function() 520 { 521 if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed) 522 this.prompt.moveCaretToEndOfPrompt(); 523 }, 524 525 _registerShortcuts: function() 526 { 527 this._shortcuts = {}; 528 529 var shortcut = WebInspector.KeyboardShortcut; 530 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console")); 531 532 var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl); 533 this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this); 534 var keys = [shortcutL]; 535 if (WebInspector.isMac()) { 536 var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta); 537 this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this); 538 keys.unshift(shortcutK); 539 } 540 section.addAlternateKeys(keys, WebInspector.UIString("Clear console")); 541 542 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix")); 543 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion")); 544 545 keys = [ 546 shortcut.makeDescriptor(shortcut.Keys.Down), 547 shortcut.makeDescriptor(shortcut.Keys.Up) 548 ]; 549 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line")); 550 551 if (WebInspector.isMac()) { 552 keys = [ 553 shortcut.makeDescriptor("N", shortcut.Modifiers.Alt), 554 shortcut.makeDescriptor("P", shortcut.Modifiers.Alt) 555 ]; 556 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command")); 557 } 558 559 section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command")); 560 }, 561 562 _requestClearMessages: function() 563 { 564 WebInspector.console.requestClearMessages(); 565 }, 566 567 _promptKeyDown: function(event) 568 { 569 if (isEnterKey(event)) { 570 this._enterKeyPressed(event); 571 return; 572 } 573 574 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); 575 var handler = this._shortcuts[shortcut]; 576 if (handler) { 577 handler(); 578 event.preventDefault(); 579 } 580 }, 581 582 /** 583 * @param {string} expression 584 * @param {boolean} showResultOnly 585 */ 586 evaluateUsingTextPrompt: function(expression, showResultOnly) 587 { 588 this._appendCommand(expression, this.prompt.text, false, showResultOnly); 589 }, 590 591 _enterKeyPressed: function(event) 592 { 593 if (event.altKey || event.ctrlKey || event.shiftKey) 594 return; 595 596 event.consume(true); 597 598 this.prompt.clearAutoComplete(true); 599 600 var str = this.prompt.text; 601 if (!str.length) 602 return; 603 this._appendCommand(str, "", true, false); 604 }, 605 606 /** 607 * @param {!WebInspector.RemoteObject} result 608 * @param {boolean} wasThrown 609 * @param {!WebInspector.ConsoleCommand} originatingCommand 610 */ 611 _printResult: function(result, wasThrown, originatingCommand) 612 { 613 if (!result) 614 return; 615 616 /** 617 * @param {string=} url 618 * @param {number=} lineNumber 619 * @param {number=} columnNumber 620 * @this {WebInspector.ConsoleView} 621 */ 622 function addMessage(url, lineNumber, columnNumber) 623 { 624 var message = new WebInspector.ConsoleCommandResult(result, wasThrown, originatingCommand, this._linkifier, url, lineNumber, columnNumber); 625 WebInspector.console.addMessage(message); 626 } 627 628 if (result.type !== "function") { 629 addMessage.call(this); 630 return; 631 } 632 633 DebuggerAgent.getFunctionDetails(result.objectId, didGetDetails.bind(this)); 634 635 /** 636 * @param {?Protocol.Error} error 637 * @param {!DebuggerAgent.FunctionDetails} response 638 * @this {WebInspector.ConsoleView} 639 */ 640 function didGetDetails(error, response) 641 { 642 if (error) { 643 console.error(error); 644 addMessage.call(this); 645 return; 646 } 647 648 var url; 649 var lineNumber; 650 var columnNumber; 651 var script = WebInspector.debuggerModel.scriptForId(response.location.scriptId); 652 if (script && script.sourceURL) { 653 url = script.sourceURL; 654 lineNumber = response.location.lineNumber + 1; 655 columnNumber = response.location.columnNumber + 1; 656 } 657 addMessage.call(this, url, lineNumber, columnNumber); 658 } 659 }, 660 661 /** 662 * @param {string} text 663 * @param {string} newPromptText 664 * @param {boolean} useCommandLineAPI 665 * @param {boolean} showResultOnly 666 */ 667 _appendCommand: function(text, newPromptText, useCommandLineAPI, showResultOnly) 668 { 669 if (!showResultOnly) { 670 var commandMessage = new WebInspector.ConsoleCommand(text); 671 WebInspector.console.addMessage(commandMessage); 672 } 673 this.prompt.text = newPromptText; 674 675 /** 676 * @param {?WebInspector.RemoteObject} result 677 * @param {boolean} wasThrown 678 * @param {?RuntimeAgent.RemoteObject=} valueResult 679 * @this {WebInspector.ConsoleView} 680 */ 681 function printResult(result, wasThrown, valueResult) 682 { 683 if (!result) 684 return; 685 686 if (!showResultOnly) { 687 this.prompt.pushHistoryItem(text); 688 WebInspector.settings.consoleHistory.set(this.prompt.historyData.slice(-30)); 689 } 690 691 this._printResult(result, wasThrown, commandMessage); 692 } 693 WebInspector.runtimeModel.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(this)); 694 695 WebInspector.userMetrics.ConsoleEvaluated.record(); 696 }, 697 698 elementsToRestoreScrollPositionsFor: function() 699 { 700 return [this.messagesElement]; 701 }, 702 703 searchCanceled: function() 704 { 705 this._clearCurrentSearchResultHighlight(); 706 delete this._searchResultsIndices; 707 delete this._searchRegex; 708 }, 709 710 /** 711 * @param {string} query 712 * @param {boolean} shouldJump 713 */ 714 performSearch: function(query, shouldJump) 715 { 716 this.searchCanceled(); 717 this._searchableView.updateSearchMatchesCount(0); 718 this._searchRegex = createPlainTextSearchRegex(query, "gi"); 719 720 this._searchResultsIndices = []; 721 for (var i = 0; i < this._visibleMessagesIndices.length; i++) { 722 if (WebInspector.console.messages[this._visibleMessagesIndices[i]].matchesRegex(this._searchRegex)) 723 this._searchResultsIndices.push(this._visibleMessagesIndices[i]); 724 } 725 this._searchableView.updateSearchMatchesCount(this._searchResultsIndices.length); 726 this._currentSearchResultIndex = -1; 727 if (shouldJump && this._searchResultsIndices.length) 728 this._jumpToSearchResult(0); 729 }, 730 731 jumpToNextSearchResult: function() 732 { 733 if (!this._searchResultsIndices || !this._searchResultsIndices.length) 734 return; 735 this._jumpToSearchResult((this._currentSearchResultIndex + 1) % this._searchResultsIndices.length); 736 }, 737 738 jumpToPreviousSearchResult: function() 739 { 740 if (!this._searchResultsIndices || !this._searchResultsIndices.length) 741 return; 742 var index = this._currentSearchResultIndex - 1; 743 if (index === -1) 744 index = this._searchResultsIndices.length - 1; 745 this._jumpToSearchResult(index); 746 }, 747 748 _clearCurrentSearchResultHighlight: function() 749 { 750 if (!this._searchResultsIndices) 751 return; 752 var highlightedMessage = WebInspector.console.messages[this._searchResultsIndices[this._currentSearchResultIndex]]; 753 if (highlightedMessage) 754 highlightedMessage.clearHighlight(); 755 this._currentSearchResultIndex = -1; 756 }, 757 758 _jumpToSearchResult: function(index) 759 { 760 this._clearCurrentSearchResultHighlight(); 761 this._currentSearchResultIndex = index; 762 this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex); 763 WebInspector.console.messages[this._searchResultsIndices[index]].highlightSearchResults(this._searchRegex); 764 }, 765 766 __proto__: WebInspector.View.prototype 767 } 768 769 /** 770 * @extends {WebInspector.Object} 771 * @constructor 772 */ 773 WebInspector.ConsoleViewFilter = function() 774 { 775 this._messageURLFilters = WebInspector.settings.messageURLFilters.get(); 776 this._filterChanged = this.dispatchEventToListeners.bind(this, WebInspector.ConsoleViewFilter.Events.FilterChanged); 777 }; 778 779 WebInspector.ConsoleViewFilter.Events = { 780 FilterChanged: "FilterChanged" 781 }; 782 783 WebInspector.ConsoleViewFilter.prototype = { 784 addFilters: function(filterBar) 785 { 786 this._textFilterUI = new WebInspector.TextFilterUI(true); 787 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this); 788 filterBar.addFilter(this._textFilterUI); 789 790 this._levelFilterUI = new WebInspector.NamedBitSetFilterUI(); 791 this._levelFilterUI.addBit("error", WebInspector.UIString("Errors")); 792 this._levelFilterUI.addBit("warning", WebInspector.UIString("Warnings")); 793 this._levelFilterUI.addBit("info", WebInspector.UIString("Info")); 794 this._levelFilterUI.addBit("log", WebInspector.UIString("Logs")); 795 this._levelFilterUI.addBit("debug", WebInspector.UIString("Debug")); 796 this._levelFilterUI.bindSetting(WebInspector.settings.messageLevelFilters); 797 this._levelFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this); 798 filterBar.addFilter(this._levelFilterUI); 799 }, 800 801 _textFilterChanged: function(event) 802 { 803 this._filterRegex = this._textFilterUI.regex(); 804 805 this._filterChanged(); 806 }, 807 808 /** 809 * @param {string} url 810 */ 811 addMessageURLFilter: function(url) 812 { 813 this._messageURLFilters[url] = true; 814 WebInspector.settings.messageURLFilters.set(this._messageURLFilters); 815 this._filterChanged(); 816 }, 817 818 /** 819 * @param {string} url 820 */ 821 removeMessageURLFilter: function(url) 822 { 823 if (!url) 824 this._messageURLFilters = {}; 825 else 826 delete this._messageURLFilters[url]; 827 828 WebInspector.settings.messageURLFilters.set(this._messageURLFilters); 829 this._filterChanged(); 830 }, 831 832 /** 833 * @returns {!Object} 834 */ 835 get messageURLFilters() 836 { 837 return this._messageURLFilters; 838 }, 839 840 /** 841 * @param {!WebInspector.ConsoleMessage} message 842 * @return {boolean} 843 */ 844 shouldBeVisible: function(message) 845 { 846 if ((message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed || message.type === WebInspector.ConsoleMessage.MessageType.EndGroup)) 847 return true; 848 849 if (message.type === WebInspector.ConsoleMessage.MessageType.Result || message.type === WebInspector.ConsoleMessage.MessageType.Command) 850 return true; 851 852 if (message.url && this._messageURLFilters[message.url]) 853 return false; 854 855 if (message.level && !this._levelFilterUI.accept(message.level)) 856 return false; 857 858 if (this._filterRegex) { 859 this._filterRegex.lastIndex = 0; 860 if (!message.matchesRegex(this._filterRegex)) 861 return false; 862 } 863 864 return true; 865 }, 866 867 reset: function() 868 { 869 this._messageURLFilters = {}; 870 WebInspector.settings.messageURLFilters.set(this._messageURLFilters); 871 WebInspector.settings.messageLevelFilters.set({}); 872 this._filterChanged(); 873 }, 874 875 __proto__: WebInspector.Object.prototype 876 }; 877 878 879 /** 880 * @constructor 881 * @extends WebInspector.ConsoleMessage 882 */ 883 WebInspector.ConsoleCommand = function(text) 884 { 885 this.text = text; 886 this.type = WebInspector.ConsoleMessage.MessageType.Command; 887 } 888 889 WebInspector.ConsoleCommand.prototype = { 890 wasShown: function() 891 { 892 }, 893 894 willHide: function() 895 { 896 }, 897 898 clearHighlight: function() 899 { 900 var highlightedMessage = this._formattedCommand; 901 delete this._formattedCommand; 902 this._formatCommand(); 903 this._element.replaceChild(this._formattedCommand, highlightedMessage); 904 }, 905 906 /** 907 * @param {!RegExp} regexObject 908 */ 909 highlightSearchResults: function(regexObject) 910 { 911 regexObject.lastIndex = 0; 912 var match = regexObject.exec(this.text); 913 var matchRanges = []; 914 while (match) { 915 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length)); 916 match = regexObject.exec(this.text); 917 } 918 WebInspector.highlightSearchResults(this._formattedCommand, matchRanges); 919 this._element.scrollIntoViewIfNeeded(); 920 }, 921 922 /** 923 * @param {!RegExp} regexObject 924 */ 925 matchesRegex: function(regexObject) 926 { 927 regexObject.lastIndex = 0; 928 return regexObject.test(this.text); 929 }, 930 931 toMessageElement: function() 932 { 933 if (!this._element) { 934 this._element = document.createElement("div"); 935 this._element.command = this; 936 this._element.className = "console-user-command"; 937 938 this._formatCommand(); 939 this._element.appendChild(this._formattedCommand); 940 } 941 return this._element; 942 }, 943 944 _formatCommand: function() 945 { 946 this._formattedCommand = document.createElement("span"); 947 this._formattedCommand.className = "console-message-text source-code"; 948 this._formattedCommand.textContent = this.text; 949 }, 950 951 __proto__: WebInspector.ConsoleMessage.prototype 952 } 953 954 /** 955 * @extends {WebInspector.ConsoleMessageImpl} 956 * @constructor 957 * @param {!WebInspector.RemoteObject} result 958 * @param {boolean} wasThrown 959 * @param {!WebInspector.ConsoleCommand} originatingCommand 960 * @param {!WebInspector.Linkifier} linkifier 961 * @param {string=} url 962 * @param {number=} lineNumber 963 * @param {number=} columnNumber 964 */ 965 WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingCommand, linkifier, url, lineNumber, columnNumber) 966 { 967 var level = (wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log); 968 this.originatingCommand = originatingCommand; 969 WebInspector.ConsoleMessageImpl.call(this, WebInspector.ConsoleMessage.MessageSource.JS, level, "", linkifier, WebInspector.ConsoleMessage.MessageType.Result, url, lineNumber, columnNumber, undefined, [result]); 970 } 971 972 WebInspector.ConsoleCommandResult.prototype = { 973 /** 974 * @override 975 * @param {!WebInspector.RemoteObject} array 976 * @return {boolean} 977 */ 978 useArrayPreviewInFormatter: function(array) 979 { 980 return false; 981 }, 982 983 toMessageElement: function() 984 { 985 var element = WebInspector.ConsoleMessageImpl.prototype.toMessageElement.call(this); 986 element.classList.add("console-user-command-result"); 987 return element; 988 }, 989 990 __proto__: WebInspector.ConsoleMessageImpl.prototype 991 } 992 993 /** 994 * @constructor 995 */ 996 WebInspector.ConsoleGroup = function(parentGroup) 997 { 998 this.parentGroup = parentGroup; 999 1000 var element = document.createElement("div"); 1001 element.className = "console-group"; 1002 element.group = this; 1003 this.element = element; 1004 1005 if (parentGroup) { 1006 var bracketElement = document.createElement("div"); 1007 bracketElement.className = "console-group-bracket"; 1008 element.appendChild(bracketElement); 1009 } 1010 1011 var messagesElement = document.createElement("div"); 1012 messagesElement.className = "console-group-messages"; 1013 element.appendChild(messagesElement); 1014 this.messagesElement = messagesElement; 1015 } 1016 1017 WebInspector.ConsoleGroup.prototype = { 1018 /** 1019 * @param {!WebInspector.ConsoleMessage} message 1020 * @param {!Node=} node 1021 */ 1022 addMessage: function(message, node) 1023 { 1024 var element = message.toMessageElement(); 1025 1026 if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) { 1027 this.messagesElement.parentNode.insertBefore(element, this.messagesElement); 1028 element.addEventListener("click", this._titleClicked.bind(this), false); 1029 var groupElement = element.enclosingNodeOrSelfWithClass("console-group"); 1030 if (groupElement && message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) 1031 groupElement.classList.add("collapsed"); 1032 } else { 1033 this.messagesElement.insertBefore(element, node || null); 1034 message.wasShown(); 1035 } 1036 1037 if (element.previousSibling && message.originatingCommand && element.previousSibling.command === message.originatingCommand) 1038 element.previousSibling.classList.add("console-adjacent-user-command-result"); 1039 }, 1040 1041 _titleClicked: function(event) 1042 { 1043 var groupTitleElement = event.target.enclosingNodeOrSelfWithClass("console-group-title"); 1044 if (groupTitleElement) { 1045 var groupElement = groupTitleElement.enclosingNodeOrSelfWithClass("console-group"); 1046 if (groupElement && !groupElement.classList.toggle("collapsed")) { 1047 if (groupElement.group) { 1048 groupElement.group.wasShown(); 1049 } 1050 } 1051 groupTitleElement.scrollIntoViewIfNeeded(true); 1052 } 1053 event.consume(true); 1054 }, 1055 1056 wasShown: function() 1057 { 1058 if (this.element.classList.contains("collapsed")) 1059 return; 1060 var node = this.messagesElement.firstChild; 1061 while (node) { 1062 if (node.classList.contains("console-message") && node.message) 1063 node.message.wasShown(); 1064 if (node.classList.contains("console-group") && node.group) 1065 node.group.wasShown(); 1066 node = node.nextSibling; 1067 } 1068 } 1069 } 1070 1071 /** 1072 * @type {!WebInspector.ConsoleView} 1073 */ 1074 WebInspector.consoleView; 1075 1076 WebInspector.ConsoleMessage.create = function(source, level, message, type, url, line, column, repeatCount, parameters, stackTrace, requestId, isOutdated) 1077 { 1078 return new WebInspector.ConsoleMessageImpl(source, level, message, WebInspector.consoleView._linkifier, type, url, line, column, repeatCount, parameters, stackTrace, requestId, isOutdated); 1079 } 1080