ConsoleView.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  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. * @extends {WebInspector.View}
  31. * @constructor
  32. * @param {boolean} hideContextSelector
  33. */
  34. WebInspector.ConsoleView = function(hideContextSelector)
  35. {
  36. WebInspector.View.call(this);
  37. this.element.id = "console-view";
  38. this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
  39. this._visibleMessages = [];
  40. this._messages = [];
  41. this._urlToMessageCount = {};
  42. this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item");
  43. this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this);
  44. this._frameSelector = new WebInspector.StatusBarComboBox(this._frameChanged.bind(this), "console-context");
  45. this._contextSelector = new WebInspector.StatusBarComboBox(this._contextChanged.bind(this), "console-context");
  46. if (hideContextSelector) {
  47. this._frameSelector.element.addStyleClass("hidden");
  48. this._contextSelector.element.addStyleClass("hidden");
  49. }
  50. this.messagesElement = document.createElement("div");
  51. this.messagesElement.id = "console-messages";
  52. this.messagesElement.className = "monospace";
  53. this.messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
  54. this.element.appendChild(this.messagesElement);
  55. this._scrolledToBottom = true;
  56. this.promptElement = document.createElement("div");
  57. this.promptElement.id = "console-prompt";
  58. this.promptElement.className = "source-code";
  59. this.promptElement.spellcheck = false;
  60. this.messagesElement.appendChild(this.promptElement);
  61. this.messagesElement.appendChild(document.createElement("br"));
  62. this.topGroup = new WebInspector.ConsoleGroup(null);
  63. this.messagesElement.insertBefore(this.topGroup.element, this.promptElement);
  64. this.currentGroup = this.topGroup;
  65. this._filterBarElement = document.createElement("div");
  66. this._filterBarElement.className = "scope-bar status-bar-item";
  67. function createDividerElement()
  68. {
  69. var dividerElement = document.createElement("div");
  70. dividerElement.addStyleClass("scope-bar-divider");
  71. this._filterBarElement.appendChild(dividerElement);
  72. }
  73. var updateFilterHandler = this._updateFilter.bind(this);
  74. function createFilterElement(category, label)
  75. {
  76. var categoryElement = document.createElement("li");
  77. categoryElement.category = category;
  78. categoryElement.className = category;
  79. categoryElement.addEventListener("click", updateFilterHandler, false);
  80. categoryElement.textContent = label;
  81. this._filterBarElement.appendChild(categoryElement);
  82. return categoryElement;
  83. }
  84. this.allElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
  85. createDividerElement.call(this);
  86. this.errorElement = createFilterElement.call(this, "errors", WebInspector.UIString("Errors"));
  87. this.warningElement = createFilterElement.call(this, "warnings", WebInspector.UIString("Warnings"));
  88. this.logElement = createFilterElement.call(this, "logs", WebInspector.UIString("Logs"));
  89. this.debugElement = createFilterElement.call(this, "debug", WebInspector.UIString("Debug"));
  90. this.filter(this.allElement, false);
  91. this._registerShortcuts();
  92. this.registerRequiredCSS("textPrompt.css");
  93. this.messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
  94. WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged.bind(this));
  95. WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
  96. WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
  97. this._linkifier = new WebInspector.Linkifier();
  98. this.prompt = new WebInspector.TextPromptWithHistory(WebInspector.runtimeModel.completionsForTextPrompt.bind(WebInspector.runtimeModel));
  99. this.prompt.setSuggestBoxEnabled("generic-suggest");
  100. this.prompt.renderAsBlock();
  101. this.prompt.attach(this.promptElement);
  102. this.prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
  103. this.prompt.setHistoryData(WebInspector.settings.consoleHistory.get());
  104. WebInspector.runtimeModel.contextLists().forEach(this._addFrame, this);
  105. WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, this._frameAdded, this);
  106. WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, this._frameRemoved, this);
  107. }
  108. WebInspector.ConsoleView.Events = {
  109. ConsoleCleared: "console-cleared",
  110. EntryAdded: "console-entry-added",
  111. }
  112. WebInspector.ConsoleView.prototype = {
  113. statusBarItems: function()
  114. {
  115. return [this._clearConsoleButton.element, this._frameSelector.element, this._contextSelector.element, this._filterBarElement];
  116. },
  117. /**
  118. * @param {WebInspector.Event} event
  119. */
  120. _frameAdded: function(event)
  121. {
  122. var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
  123. this._addFrame(contextList);
  124. },
  125. /**
  126. * @param {WebInspector.FrameExecutionContextList} contextList
  127. */
  128. _addFrame: function(contextList)
  129. {
  130. var option = this._frameSelector.createOption(contextList.displayName, contextList.url);
  131. option._contextList = contextList;
  132. contextList._consoleOption = option;
  133. contextList.addEventListener(WebInspector.FrameExecutionContextList.EventTypes.ContextsUpdated, this._frameUpdated, this);
  134. contextList.addEventListener(WebInspector.FrameExecutionContextList.EventTypes.ContextAdded, this._contextAdded, this);
  135. this._frameChanged();
  136. },
  137. /**
  138. * @param {WebInspector.Event} event
  139. */
  140. _frameRemoved: function(event)
  141. {
  142. var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
  143. this._frameSelector.removeOption(contextList._consoleOption);
  144. this._frameChanged();
  145. },
  146. _frameChanged: function()
  147. {
  148. var context = this._currentFrame();
  149. if (!context) {
  150. WebInspector.runtimeModel.setCurrentExecutionContext(null);
  151. this._contextSelector.element.addStyleClass("hidden");
  152. return;
  153. }
  154. var executionContexts = context.executionContexts();
  155. if (executionContexts.length)
  156. WebInspector.runtimeModel.setCurrentExecutionContext(executionContexts[0]);
  157. if (executionContexts.length === 1) {
  158. this._contextSelector.element.addStyleClass("hidden");
  159. return;
  160. }
  161. this._contextSelector.element.removeStyleClass("hidden");
  162. this._contextSelector.removeOptions();
  163. for (var i = 0; i < executionContexts.length; i++)
  164. this._appendContextOption(executionContexts[i]);
  165. },
  166. /**
  167. * @param {WebInspector.ExecutionContext} executionContext
  168. */
  169. _appendContextOption: function(executionContext)
  170. {
  171. if (!WebInspector.runtimeModel.currentExecutionContext())
  172. WebInspector.runtimeModel.setCurrentExecutionContext(executionContext);
  173. var option = this._contextSelector.createOption(executionContext.name, executionContext.id);
  174. option._executionContext = executionContext;
  175. },
  176. /**
  177. * @param {Event} event
  178. */
  179. _contextChanged: function(event)
  180. {
  181. var option = this._contextSelector.selectedOption();
  182. WebInspector.runtimeModel.setCurrentExecutionContext(option ? option._executionContext : null);
  183. },
  184. /**
  185. * @param {WebInspector.Event} event
  186. */
  187. _frameUpdated: function(event)
  188. {
  189. var contextList = /** {WebInspector.FrameExecutionContextList */ event.data;
  190. var option = contextList._consoleOption;
  191. option.text = contextList.displayName;
  192. option.title = contextList.url;
  193. },
  194. /**
  195. * @param {WebInspector.Event} event
  196. */
  197. _contextAdded: function(event)
  198. {
  199. var contextList = /** {WebInspector.FrameExecutionContextList */ event.data;
  200. if (contextList === this._currentFrame())
  201. this._frameChanged();
  202. },
  203. /**
  204. * @return {WebInspector.FrameExecutionContextList|undefined}
  205. */
  206. _currentFrame: function()
  207. {
  208. var option = this._frameSelector.selectedOption();
  209. return option ? option._contextList : undefined;
  210. },
  211. _updateFilter: function(e)
  212. {
  213. var isMac = WebInspector.isMac();
  214. var selectMultiple = false;
  215. if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
  216. selectMultiple = true;
  217. if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
  218. selectMultiple = true;
  219. this.filter(e.target, selectMultiple);
  220. },
  221. filter: function(target, selectMultiple)
  222. {
  223. function unselectAll()
  224. {
  225. this.allElement.removeStyleClass("selected");
  226. this.errorElement.removeStyleClass("selected");
  227. this.warningElement.removeStyleClass("selected");
  228. this.logElement.removeStyleClass("selected");
  229. this.debugElement.removeStyleClass("selected");
  230. this.messagesElement.classList.remove("filter-all", "filter-errors", "filter-warnings", "filter-logs", "filter-debug");
  231. }
  232. var targetFilterClass = "filter-" + target.category;
  233. if (target.category === "all") {
  234. if (target.hasStyleClass("selected")) {
  235. // We can't unselect all, so we break early here
  236. return;
  237. }
  238. unselectAll.call(this);
  239. } else {
  240. // Something other than all is being selected, so we want to unselect all
  241. if (this.allElement.hasStyleClass("selected")) {
  242. this.allElement.removeStyleClass("selected");
  243. this.messagesElement.removeStyleClass("filter-all");
  244. }
  245. }
  246. if (!selectMultiple) {
  247. // If multiple selection is off, we want to unselect everything else
  248. // and just select ourselves.
  249. unselectAll.call(this);
  250. target.addStyleClass("selected");
  251. this.messagesElement.addStyleClass(targetFilterClass);
  252. return;
  253. }
  254. if (target.hasStyleClass("selected")) {
  255. // If selectMultiple is turned on, and we were selected, we just
  256. // want to unselect ourselves.
  257. target.removeStyleClass("selected");
  258. this.messagesElement.removeStyleClass(targetFilterClass);
  259. } else {
  260. // If selectMultiple is turned on, and we weren't selected, we just
  261. // want to select ourselves.
  262. target.addStyleClass("selected");
  263. this.messagesElement.addStyleClass(targetFilterClass);
  264. }
  265. },
  266. willHide: function()
  267. {
  268. this.prompt.hideSuggestBox();
  269. this.prompt.clearAutoComplete(true);
  270. },
  271. wasShown: function()
  272. {
  273. if (!this.prompt.isCaretInsidePrompt())
  274. this.prompt.moveCaretToEndOfPrompt();
  275. },
  276. afterShow: function()
  277. {
  278. WebInspector.setCurrentFocusElement(this.promptElement);
  279. },
  280. storeScrollPositions: function()
  281. {
  282. WebInspector.View.prototype.storeScrollPositions.call(this);
  283. this._scrolledToBottom = this.messagesElement.isScrolledToBottom();
  284. },
  285. restoreScrollPositions: function()
  286. {
  287. if (this._scrolledToBottom)
  288. this._immediatelyScrollIntoView();
  289. else
  290. WebInspector.View.prototype.restoreScrollPositions.call(this);
  291. },
  292. onResize: function()
  293. {
  294. this.restoreScrollPositions();
  295. },
  296. _isScrollIntoViewScheduled: function()
  297. {
  298. return !!this._scrollIntoViewTimer;
  299. },
  300. _scheduleScrollIntoView: function()
  301. {
  302. if (this._scrollIntoViewTimer)
  303. return;
  304. function scrollIntoView()
  305. {
  306. delete this._scrollIntoViewTimer;
  307. this.promptElement.scrollIntoView(true);
  308. }
  309. this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 20);
  310. },
  311. _immediatelyScrollIntoView: function()
  312. {
  313. this.promptElement.scrollIntoView(true);
  314. this._cancelScheduledScrollIntoView();
  315. },
  316. _cancelScheduledScrollIntoView: function()
  317. {
  318. if (!this._isScrollIntoViewScheduled())
  319. return;
  320. clearTimeout(this._scrollIntoViewTimer);
  321. delete this._scrollIntoViewTimer;
  322. },
  323. /**
  324. * @param {WebInspector.Event} event
  325. */
  326. _consoleMessageAdded: function(event)
  327. {
  328. var message = /** @type {WebInspector.ConsoleMessage} */ (event.data);
  329. this._messages.push(message);
  330. if (this._urlToMessageCount[message.url])
  331. this._urlToMessageCount[message.url]++;
  332. else
  333. this._urlToMessageCount[message.url] = 1;
  334. if (this._shouldBeVisible(message))
  335. this._appendConsoleMessage(message);
  336. },
  337. _appendConsoleMessage: function(message)
  338. {
  339. // this.messagesElement.isScrolledToBottom() is forcing style recalculation.
  340. // We just skip it if the scroll action has been scheduled.
  341. if (!this._isScrollIntoViewScheduled() && ((message instanceof WebInspector.ConsoleCommandResult) || this.messagesElement.isScrolledToBottom()))
  342. this._scheduleScrollIntoView();
  343. this._visibleMessages.push(message);
  344. if (message.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
  345. var parentGroup = this.currentGroup.parentGroup
  346. if (parentGroup)
  347. this.currentGroup = parentGroup;
  348. } else {
  349. if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
  350. var group = new WebInspector.ConsoleGroup(this.currentGroup);
  351. this.currentGroup.messagesElement.appendChild(group.element);
  352. this.currentGroup = group;
  353. message.group = group;
  354. }
  355. this.currentGroup.addMessage(message);
  356. }
  357. this.dispatchEventToListeners(WebInspector.ConsoleView.Events.EntryAdded, message);
  358. },
  359. _consoleCleared: function()
  360. {
  361. this._scrolledToBottom = true;
  362. for (var i = 0; i < this._visibleMessages.length; ++i)
  363. this._visibleMessages[i].willHide();
  364. this._visibleMessages = [];
  365. this._messages = [];
  366. this.currentGroup = this.topGroup;
  367. this.topGroup.messagesElement.removeChildren();
  368. this.dispatchEventToListeners(WebInspector.ConsoleView.Events.ConsoleCleared);
  369. this._linkifier.reset();
  370. },
  371. _handleContextMenuEvent: function(event)
  372. {
  373. if (!window.getSelection().isCollapsed) {
  374. // If there is a selection, we want to show our normal context menu
  375. // (with Copy, etc.), and not Clear Console.
  376. return;
  377. }
  378. if (event.target.enclosingNodeOrSelfWithNodeName("a"))
  379. return;
  380. var contextMenu = new WebInspector.ContextMenu(event);
  381. function monitoringXHRItemAction()
  382. {
  383. WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
  384. }
  385. contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction.bind(this), WebInspector.settings.monitoringXHREnabled.get());
  386. function preserveLogItemAction()
  387. {
  388. WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get());
  389. }
  390. contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction.bind(this), WebInspector.settings.preserveConsoleLog.get());
  391. var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message");
  392. var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
  393. if (sourceElement && sourceElement.message.url)
  394. filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(sourceElement.message.url).displayName), this._addMessageURLFilter.bind(this, sourceElement.message.url));
  395. filterSubMenu.appendSeparator();
  396. var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._removeMessageURLFilter.bind(this));
  397. filterSubMenu.appendSeparator();
  398. var hasFilters = false;
  399. for (var url in this._messageURLFilters) {
  400. if (this._messageURLFilters.hasOwnProperty(url)) {
  401. filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._removeMessageURLFilter.bind(this, url), true);
  402. hasFilters = true;
  403. }
  404. }
  405. filterSubMenu.setEnabled(hasFilters || (sourceElement && sourceElement.message.url));
  406. unhideAll.setEnabled(hasFilters);
  407. contextMenu.appendSeparator();
  408. contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
  409. var messageElement = event.target.enclosingNodeOrSelfWithClass("console-message");
  410. var request = (messageElement && messageElement.message) ? messageElement.message.request() : null;
  411. if (request && request.type === WebInspector.resourceTypes.XHR) {
  412. contextMenu.appendSeparator();
  413. contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
  414. }
  415. contextMenu.show();
  416. },
  417. /**
  418. * @param {string} url
  419. * @private
  420. */
  421. _addMessageURLFilter: function(url)
  422. {
  423. this._messageURLFilters[url] = true;
  424. WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
  425. this._updateMessageList();
  426. },
  427. /**
  428. * @param {string} url
  429. * @private
  430. */
  431. _removeMessageURLFilter: function(url)
  432. {
  433. if (!url)
  434. this._messageURLFilters = {};
  435. else
  436. delete this._messageURLFilters[url];
  437. WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
  438. this._updateMessageList();
  439. },
  440. /**
  441. * @param {WebInspector.ConsoleMessage} message
  442. * @return {boolean}
  443. * @private
  444. */
  445. _shouldBeVisible: function(message)
  446. {
  447. return (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed || message.type === WebInspector.ConsoleMessage.MessageType.EndGroup) ||
  448. (!message.url || !this._messageURLFilters[message.url]);
  449. },
  450. /**
  451. * @private
  452. */
  453. _updateMessageList: function()
  454. {
  455. var group = this.topGroup;
  456. var sourceMessages = this._messages;
  457. var visibleMessageIndex = 0;
  458. var newVisibleMessages = [];
  459. var anchor = null;
  460. for (var i = 0; i < sourceMessages.length; i++) {
  461. var sourceMessage = sourceMessages[i];
  462. var visibleMessage = this._visibleMessages[visibleMessageIndex];
  463. if (visibleMessage === sourceMessage) {
  464. visibleMessageIndex++;
  465. if (this._shouldBeVisible(visibleMessage)) {
  466. newVisibleMessages.push(visibleMessage);
  467. if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
  468. anchor = group.element;
  469. group = group.parentGroup || group;
  470. } else if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroup || sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
  471. group = sourceMessage.group;
  472. anchor = group.messagesElement.firstChild;
  473. }
  474. } else {
  475. visibleMessage.willHide();
  476. visibleMessage.toMessageElement().removeSelf();
  477. }
  478. } else {
  479. if (this._shouldBeVisible(sourceMessage)) {
  480. group.addMessage(sourceMessage, anchor ? anchor.nextSibling : group.messagesElement.firstChild);
  481. newVisibleMessages.push(sourceMessage);
  482. anchor = sourceMessage.toMessageElement();
  483. }
  484. }
  485. }
  486. this._visibleMessages = newVisibleMessages;
  487. },
  488. _monitoringXHREnabledSettingChanged: function(event)
  489. {
  490. ConsoleAgent.setMonitoringXHREnabled(event.data);
  491. },
  492. _messagesClicked: function(event)
  493. {
  494. if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
  495. this.prompt.moveCaretToEndOfPrompt();
  496. },
  497. _registerShortcuts: function()
  498. {
  499. this._shortcuts = {};
  500. var shortcut = WebInspector.KeyboardShortcut;
  501. var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
  502. var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
  503. this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
  504. var keys = [shortcutL];
  505. if (WebInspector.isMac()) {
  506. var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
  507. this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
  508. keys.unshift(shortcutK);
  509. }
  510. section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
  511. section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
  512. section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
  513. keys = [
  514. shortcut.makeDescriptor(shortcut.Keys.Down),
  515. shortcut.makeDescriptor(shortcut.Keys.Up)
  516. ];
  517. section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
  518. if (WebInspector.isMac()) {
  519. keys = [
  520. shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
  521. shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
  522. ];
  523. section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
  524. }
  525. section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
  526. },
  527. _requestClearMessages: function()
  528. {
  529. WebInspector.console.requestClearMessages();
  530. },
  531. _promptKeyDown: function(event)
  532. {
  533. if (isEnterKey(event)) {
  534. this._enterKeyPressed(event);
  535. return;
  536. }
  537. var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
  538. var handler = this._shortcuts[shortcut];
  539. if (handler) {
  540. handler();
  541. event.preventDefault();
  542. return;
  543. }
  544. },
  545. evaluateUsingTextPrompt: function(expression, showResultOnly)
  546. {
  547. this._appendCommand(expression, this.prompt.text, false, showResultOnly);
  548. },
  549. _enterKeyPressed: function(event)
  550. {
  551. if (event.altKey || event.ctrlKey || event.shiftKey)
  552. return;
  553. event.consume(true);
  554. this.prompt.clearAutoComplete(true);
  555. var str = this.prompt.text;
  556. if (!str.length)
  557. return;
  558. this._appendCommand(str, "", true, false);
  559. },
  560. _printResult: function(result, wasThrown, originatingCommand)
  561. {
  562. if (!result)
  563. return;
  564. var message = new WebInspector.ConsoleCommandResult(result, wasThrown, originatingCommand, this._linkifier);
  565. this._messages.push(message);
  566. this._appendConsoleMessage(message);
  567. },
  568. _appendCommand: function(text, newPromptText, useCommandLineAPI, showResultOnly)
  569. {
  570. if (!showResultOnly) {
  571. var commandMessage = new WebInspector.ConsoleCommand(text);
  572. WebInspector.console.interruptRepeatCount();
  573. this._messages.push(commandMessage);
  574. this._appendConsoleMessage(commandMessage);
  575. }
  576. this.prompt.text = newPromptText;
  577. function printResult(result, wasThrown)
  578. {
  579. if (!result)
  580. return;
  581. if (!showResultOnly) {
  582. this.prompt.pushHistoryItem(text);
  583. WebInspector.settings.consoleHistory.set(this.prompt.historyData.slice(-30));
  584. }
  585. this._printResult(result, wasThrown, commandMessage);
  586. }
  587. WebInspector.runtimeModel.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(this));
  588. WebInspector.userMetrics.ConsoleEvaluated.record();
  589. },
  590. elementsToRestoreScrollPositionsFor: function()
  591. {
  592. return [this.messagesElement];
  593. },
  594. __proto__: WebInspector.View.prototype
  595. }
  596. /**
  597. * @constructor
  598. */
  599. WebInspector.ConsoleCommand = function(command)
  600. {
  601. this.command = command;
  602. }
  603. WebInspector.ConsoleCommand.prototype = {
  604. wasShown: function()
  605. {
  606. },
  607. willHide: function()
  608. {
  609. },
  610. clearHighlight: function()
  611. {
  612. var highlightedMessage = this._formattedCommand;
  613. delete this._formattedCommand;
  614. this._formatCommand();
  615. this._element.replaceChild(this._formattedCommand, highlightedMessage);
  616. },
  617. highlightSearchResults: function(regexObject)
  618. {
  619. regexObject.lastIndex = 0;
  620. var text = this.command;
  621. var match = regexObject.exec(text);
  622. var offset = 0;
  623. var matchRanges = [];
  624. while (match) {
  625. matchRanges.push({ offset: match.index, length: match[0].length });
  626. match = regexObject.exec(text);
  627. }
  628. WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
  629. this._element.scrollIntoViewIfNeeded();
  630. },
  631. matchesRegex: function(regexObject)
  632. {
  633. return regexObject.test(this.command);
  634. },
  635. toMessageElement: function()
  636. {
  637. if (!this._element) {
  638. this._element = document.createElement("div");
  639. this._element.command = this;
  640. this._element.className = "console-user-command";
  641. this._formatCommand();
  642. this._element.appendChild(this._formattedCommand);
  643. }
  644. return this._element;
  645. },
  646. _formatCommand: function()
  647. {
  648. this._formattedCommand = document.createElement("span");
  649. this._formattedCommand.className = "console-message-text source-code";
  650. this._formattedCommand.textContent = this.command;
  651. },
  652. }
  653. /**
  654. * @extends {WebInspector.ConsoleMessageImpl}
  655. * @constructor
  656. * @param {WebInspector.Linkifier} linkifier
  657. */
  658. WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingCommand, linkifier)
  659. {
  660. var level = (wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log);
  661. this.originatingCommand = originatingCommand;
  662. WebInspector.ConsoleMessageImpl.call(this, WebInspector.ConsoleMessage.MessageSource.JS, level, "", linkifier, WebInspector.ConsoleMessage.MessageType.Result, undefined, undefined, undefined, [result]);
  663. }
  664. WebInspector.ConsoleCommandResult.prototype = {
  665. /**
  666. * @override
  667. * @param {WebInspector.RemoteObject} array
  668. * @return {boolean}
  669. */
  670. useArrayPreviewInFormatter: function(array)
  671. {
  672. return false;
  673. },
  674. toMessageElement: function()
  675. {
  676. var element = WebInspector.ConsoleMessageImpl.prototype.toMessageElement.call(this);
  677. element.addStyleClass("console-user-command-result");
  678. return element;
  679. },
  680. __proto__: WebInspector.ConsoleMessageImpl.prototype
  681. }
  682. /**
  683. * @constructor
  684. */
  685. WebInspector.ConsoleGroup = function(parentGroup)
  686. {
  687. this.parentGroup = parentGroup;
  688. var element = document.createElement("div");
  689. element.className = "console-group";
  690. element.group = this;
  691. this.element = element;
  692. if (parentGroup) {
  693. var bracketElement = document.createElement("div");
  694. bracketElement.className = "console-group-bracket";
  695. element.appendChild(bracketElement);
  696. }
  697. var messagesElement = document.createElement("div");
  698. messagesElement.className = "console-group-messages";
  699. element.appendChild(messagesElement);
  700. this.messagesElement = messagesElement;
  701. }
  702. WebInspector.ConsoleGroup.prototype = {
  703. /**
  704. * @param {WebInspector.ConsoleMessage} message
  705. * @param {Node=} node
  706. */
  707. addMessage: function(message, node)
  708. {
  709. var element = message.toMessageElement();
  710. if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
  711. this.messagesElement.parentNode.insertBefore(element, this.messagesElement);
  712. element.addEventListener("click", this._titleClicked.bind(this), false);
  713. var groupElement = element.enclosingNodeOrSelfWithClass("console-group");
  714. if (groupElement && message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
  715. groupElement.addStyleClass("collapsed");
  716. } else {
  717. this.messagesElement.insertBefore(element, node || null);
  718. message.wasShown();
  719. }
  720. if (element.previousSibling && message.originatingCommand && element.previousSibling.command === message.originatingCommand)
  721. element.previousSibling.addStyleClass("console-adjacent-user-command-result");
  722. },
  723. _titleClicked: function(event)
  724. {
  725. var groupTitleElement = event.target.enclosingNodeOrSelfWithClass("console-group-title");
  726. if (groupTitleElement) {
  727. var groupElement = groupTitleElement.enclosingNodeOrSelfWithClass("console-group");
  728. if (groupElement)
  729. if (groupElement.hasStyleClass("collapsed"))
  730. groupElement.removeStyleClass("collapsed");
  731. else
  732. groupElement.addStyleClass("collapsed");
  733. groupTitleElement.scrollIntoViewIfNeeded(true);
  734. }
  735. event.consume(true);
  736. }
  737. }
  738. /**
  739. * @type {?WebInspector.ConsoleView}
  740. */
  741. WebInspector.consoleView = null;
  742. WebInspector.ConsoleMessage.create = function(source, level, message, type, url, line, repeatCount, parameters, stackTrace, requestId, isOutdated)
  743. {
  744. return new WebInspector.ConsoleMessageImpl(source, level, message, WebInspector.consoleView._linkifier, type, url, line, repeatCount, parameters, stackTrace, requestId, isOutdated);
  745. }