ConsoleMessage.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. /*
  2. * Copyright (C) 2011 Google Inc. All rights reserved.
  3. * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
  4. * Copyright (C) 2009 Joseph Pecoraro
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  16. * its contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  20. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  23. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /**
  31. * @constructor
  32. * @extends {WebInspector.ConsoleMessage}
  33. *
  34. * @param {string} source
  35. * @param {string} level
  36. * @param {string} message
  37. * @param {WebInspector.Linkifier} linkifier
  38. * @param {string=} type
  39. * @param {string=} url
  40. * @param {number=} line
  41. * @param {number=} column
  42. * @param {number=} repeatCount
  43. * @param {Array.<RuntimeAgent.RemoteObject>=} parameters
  44. * @param {ConsoleAgent.StackTrace=} stackTrace
  45. * @param {NetworkAgent.RequestId=} requestId
  46. * @param {boolean=} isOutdated
  47. */
  48. WebInspector.ConsoleMessageImpl = function(source, level, message, linkifier, type, url, line, column, repeatCount, parameters, stackTrace, requestId, isOutdated)
  49. {
  50. WebInspector.ConsoleMessage.call(this, source, level, url, line, column, repeatCount);
  51. this._linkifier = linkifier;
  52. this.type = type || WebInspector.ConsoleMessage.MessageType.Log;
  53. this._messageText = message;
  54. this._parameters = parameters;
  55. this._stackTrace = stackTrace;
  56. this._request = requestId ? WebInspector.networkLog.requestForId(requestId) : null;
  57. this._isOutdated = isOutdated;
  58. /** @type {!Array.<!WebInspector.DataGrid>} */
  59. this._dataGrids = [];
  60. /** @type {!Map.<!WebInspector.DataGrid, Element>} */
  61. this._dataGridParents = new Map();
  62. this._customFormatters = {
  63. "object": this._formatParameterAsObject,
  64. "array": this._formatParameterAsArray,
  65. "node": this._formatParameterAsNode,
  66. "string": this._formatParameterAsString
  67. };
  68. }
  69. WebInspector.ConsoleMessageImpl.prototype = {
  70. wasShown: function()
  71. {
  72. for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
  73. var dataGrid = this._dataGrids[i];
  74. var parentElement = this._dataGridParents.get(dataGrid) || null;
  75. dataGrid.show(parentElement);
  76. }
  77. },
  78. willHide: function()
  79. {
  80. for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
  81. var dataGrid = this._dataGrids[i];
  82. this._dataGridParents.put(dataGrid, dataGrid.element.parentElement);
  83. dataGrid.detach();
  84. }
  85. },
  86. _formatMessage: function()
  87. {
  88. this._formattedMessage = document.createElement("span");
  89. this._formattedMessage.className = "console-message-text source-code";
  90. if (this.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) {
  91. switch (this.type) {
  92. case WebInspector.ConsoleMessage.MessageType.Trace:
  93. this._messageElement = this._format(this._parameters || ["console.trace()"]);
  94. break;
  95. case WebInspector.ConsoleMessage.MessageType.Clear:
  96. this._messageElement = document.createTextNode(WebInspector.UIString("Console was cleared"));
  97. this._formattedMessage.addStyleClass("console-info");
  98. break;
  99. case WebInspector.ConsoleMessage.MessageType.Assert:
  100. var args = [WebInspector.UIString("Assertion failed:")];
  101. if (this._parameters)
  102. args = args.concat(this._parameters);
  103. this._messageElement = this._format(args);
  104. break;
  105. case WebInspector.ConsoleMessage.MessageType.Dir:
  106. var obj = this._parameters ? this._parameters[0] : undefined;
  107. var args = ["%O", obj];
  108. this._messageElement = this._format(args);
  109. break;
  110. case WebInspector.ConsoleMessage.MessageType.Profile:
  111. var title = WebInspector.ProfilesPanelDescriptor.resolveProfileTitle(this._messageText);
  112. this._messageElement = document.createTextNode(WebInspector.UIString("Profile '%s' started.", title));
  113. break;
  114. case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
  115. var hashIndex = this._messageText.lastIndexOf("#");
  116. var title = WebInspector.ProfilesPanelDescriptor.resolveProfileTitle(this._messageText.substring(0, hashIndex));
  117. var uid = this._messageText.substring(hashIndex + 1);
  118. var format = WebInspector.UIString("Profile '%s' finished.", "%_");
  119. var link = WebInspector.linkifyURLAsNode("webkit-profile://CPU/" + uid, title);
  120. this._messageElement = document.createElement("span");
  121. this._formatWithSubstitutionString(format, [link], this._messageElement);
  122. break;
  123. default:
  124. var args = this._parameters || [this._messageText];
  125. this._messageElement = this._format(args);
  126. }
  127. } else if (this.source === WebInspector.ConsoleMessage.MessageSource.Network) {
  128. if (this._request) {
  129. this._stackTrace = this._request.initiator.stackTrace;
  130. if (this._request.initiator && this._request.initiator.url) {
  131. this.url = this._request.initiator.url;
  132. this.line = this._request.initiator.lineNumber;
  133. }
  134. this._messageElement = document.createElement("span");
  135. if (this.level === WebInspector.ConsoleMessage.MessageLevel.Error) {
  136. this._messageElement.appendChild(document.createTextNode(this._request.requestMethod + " "));
  137. this._messageElement.appendChild(WebInspector.linkifyRequestAsNode(this._request));
  138. if (this._request.failed)
  139. this._messageElement.appendChild(document.createTextNode(" " + this._request.localizedFailDescription));
  140. else
  141. this._messageElement.appendChild(document.createTextNode(" " + this._request.statusCode + " (" + this._request.statusText + ")"));
  142. } else {
  143. var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier(this._messageText, WebInspector.linkifyRequestAsNode.bind(null, this._request));
  144. this._messageElement.appendChild(fragment);
  145. }
  146. } else {
  147. if (this.url) {
  148. var isExternal = !WebInspector.resourceForURL(this.url) && !WebInspector.workspace.uiSourceCodeForURL(this.url);
  149. this._anchorElement = WebInspector.linkifyURLAsNode(this.url, this.url, "console-message-url", isExternal);
  150. }
  151. this._messageElement = this._format([this._messageText]);
  152. }
  153. } else {
  154. var args = this._parameters || [this._messageText];
  155. this._messageElement = this._format(args);
  156. }
  157. if (this.source !== WebInspector.ConsoleMessage.MessageSource.Network || this._request) {
  158. if (this._stackTrace && this._stackTrace.length && this._stackTrace[0].scriptId) {
  159. this._anchorElement = this._linkifyCallFrame(this._stackTrace[0]);
  160. } else if (this.url && this.url !== "undefined") {
  161. this._anchorElement = this._linkifyLocation(this.url, this.line, this.column);
  162. }
  163. }
  164. this._formattedMessage.appendChild(this._messageElement);
  165. if (this._anchorElement) {
  166. this._formattedMessage.appendChild(document.createTextNode(" "));
  167. this._formattedMessage.appendChild(this._anchorElement);
  168. }
  169. var dumpStackTrace = !!this._stackTrace && this._stackTrace.length && (this.source === WebInspector.ConsoleMessage.MessageSource.Network || this.level === WebInspector.ConsoleMessage.MessageLevel.Error || this.type === WebInspector.ConsoleMessage.MessageType.Trace);
  170. if (dumpStackTrace) {
  171. var ol = document.createElement("ol");
  172. ol.className = "outline-disclosure";
  173. var treeOutline = new TreeOutline(ol);
  174. var content = this._formattedMessage;
  175. var root = new TreeElement(content, null, true);
  176. content.treeElementForTest = root;
  177. treeOutline.appendChild(root);
  178. if (this.type === WebInspector.ConsoleMessage.MessageType.Trace)
  179. root.expand();
  180. this._populateStackTraceTreeElement(root);
  181. this._formattedMessage = ol;
  182. }
  183. // This is used for inline message bubbles in SourceFrames, or other plain-text representations.
  184. this._message = this._messageElement.textContent;
  185. },
  186. /**
  187. * @return {string}
  188. */
  189. get message()
  190. {
  191. // force message formatting
  192. var formattedMessage = this.formattedMessage;
  193. return this._message;
  194. },
  195. /**
  196. * @return {Element}
  197. */
  198. get formattedMessage()
  199. {
  200. if (!this._formattedMessage)
  201. this._formatMessage();
  202. return this._formattedMessage;
  203. },
  204. /**
  205. * @return {?WebInspector.NetworkRequest}
  206. */
  207. request: function()
  208. {
  209. return this._request;
  210. },
  211. /**
  212. * @param {string} url
  213. * @param {number} lineNumber
  214. * @param {number} columnNumber
  215. * @return {Element}
  216. */
  217. _linkifyLocation: function(url, lineNumber, columnNumber)
  218. {
  219. // FIXME(62725): stack trace line/column numbers are one-based.
  220. lineNumber = lineNumber ? lineNumber - 1 : 0;
  221. columnNumber = columnNumber ? columnNumber - 1 : 0;
  222. return this._linkifier.linkifyLocation(url, lineNumber, columnNumber, "console-message-url");
  223. },
  224. /**
  225. * @param {!ConsoleAgent.CallFrame} callFrame
  226. * @return {Element}
  227. */
  228. _linkifyCallFrame: function(callFrame)
  229. {
  230. // FIXME(62725): stack trace line/column numbers are one-based.
  231. var lineNumber = callFrame.lineNumber ? callFrame.lineNumber - 1 : 0;
  232. var columnNumber = callFrame.columnNumber ? callFrame.columnNumber - 1 : 0;
  233. var rawLocation = new WebInspector.DebuggerModel.Location(callFrame.scriptId, lineNumber, columnNumber);
  234. return this._linkifier.linkifyRawLocation(rawLocation, "console-message-url");
  235. },
  236. /**
  237. * @return {boolean}
  238. */
  239. isErrorOrWarning: function()
  240. {
  241. return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
  242. },
  243. _format: function(parameters)
  244. {
  245. // This node is used like a Builder. Values are continually appended onto it.
  246. var formattedResult = document.createElement("span");
  247. if (!parameters.length)
  248. return formattedResult;
  249. // Formatting code below assumes that parameters are all wrappers whereas frontend console
  250. // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
  251. for (var i = 0; i < parameters.length; ++i) {
  252. // FIXME: Only pass runtime wrappers here.
  253. if (parameters[i] instanceof WebInspector.RemoteObject)
  254. continue;
  255. if (typeof parameters[i] === "object")
  256. parameters[i] = WebInspector.RemoteObject.fromPayload(parameters[i]);
  257. else
  258. parameters[i] = WebInspector.RemoteObject.fromPrimitiveValue(parameters[i]);
  259. }
  260. // There can be string log and string eval result. We distinguish between them based on message type.
  261. var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && this.type !== WebInspector.ConsoleMessage.MessageType.Result;
  262. // Multiple parameters with the first being a format string. Save unused substitutions.
  263. if (shouldFormatMessage) {
  264. // Multiple parameters with the first being a format string. Save unused substitutions.
  265. var result = this._formatWithSubstitutionString(parameters[0].description, parameters.slice(1), formattedResult);
  266. parameters = result.unusedSubstitutions;
  267. if (parameters.length)
  268. formattedResult.appendChild(document.createTextNode(" "));
  269. }
  270. if (this.type === WebInspector.ConsoleMessage.MessageType.Table) {
  271. formattedResult.appendChild(this._formatParameterAsTable(parameters));
  272. return formattedResult;
  273. }
  274. // Single parameter, or unused substitutions from above.
  275. for (var i = 0; i < parameters.length; ++i) {
  276. // Inline strings when formatting.
  277. if (shouldFormatMessage && parameters[i].type === "string")
  278. formattedResult.appendChild(WebInspector.linkifyStringAsFragment(parameters[i].description));
  279. else
  280. formattedResult.appendChild(this._formatParameter(parameters[i], false, true));
  281. if (i < parameters.length - 1)
  282. formattedResult.appendChild(document.createTextNode(" "));
  283. }
  284. return formattedResult;
  285. },
  286. /**
  287. * @param {Object} output
  288. * @param {boolean=} forceObjectFormat
  289. * @param {boolean=} includePreview
  290. */
  291. _formatParameter: function(output, forceObjectFormat, includePreview)
  292. {
  293. var type;
  294. if (forceObjectFormat)
  295. type = "object";
  296. else if (output instanceof WebInspector.RemoteObject)
  297. type = output.subtype || output.type;
  298. else
  299. type = typeof output;
  300. var formatter = this._customFormatters[type];
  301. if (!formatter) {
  302. formatter = this._formatParameterAsValue;
  303. output = output.description;
  304. }
  305. var span = document.createElement("span");
  306. span.className = "console-formatted-" + type + " source-code";
  307. formatter.call(this, output, span, includePreview);
  308. return span;
  309. },
  310. _formatParameterAsValue: function(val, elem)
  311. {
  312. elem.appendChild(document.createTextNode(val));
  313. },
  314. _formatParameterAsObject: function(obj, elem, includePreview)
  315. {
  316. this._formatParameterAsArrayOrObject(obj, obj.description, elem, includePreview);
  317. },
  318. /**
  319. * @param {WebInspector.RemoteObject} obj
  320. * @param {string} description
  321. * @param {Element} elem
  322. * @param {boolean} includePreview
  323. */
  324. _formatParameterAsArrayOrObject: function(obj, description, elem, includePreview)
  325. {
  326. var titleElement = document.createElement("span");
  327. if (description)
  328. titleElement.createTextChild(description);
  329. if (includePreview && obj.preview) {
  330. titleElement.addStyleClass("console-object-preview");
  331. var lossless = this._appendObjectPreview(obj, description, titleElement);
  332. if (lossless) {
  333. elem.appendChild(titleElement);
  334. return;
  335. }
  336. }
  337. var section = new WebInspector.ObjectPropertiesSection(obj, titleElement);
  338. section.enableContextMenu();
  339. elem.appendChild(section.element);
  340. var note = section.titleElement.createChild("span", "object-info-state-note");
  341. note.title = WebInspector.UIString("Object state below is captured upon first expansion");
  342. },
  343. /**
  344. * @param {WebInspector.RemoteObject} obj
  345. * @param {string} description
  346. * @param {Element} titleElement
  347. * @return {boolean} true iff preview captured all information.
  348. */
  349. _appendObjectPreview: function(obj, description, titleElement)
  350. {
  351. var preview = obj.preview;
  352. var isArray = obj.subtype === "array";
  353. if (description)
  354. titleElement.createTextChild(" ");
  355. titleElement.createTextChild(isArray ? "[" : "{");
  356. for (var i = 0; i < preview.properties.length; ++i) {
  357. if (i > 0)
  358. titleElement.createTextChild(", ");
  359. var property = preview.properties[i];
  360. if (!isArray || property.name != i) {
  361. titleElement.createChild("span", "name").textContent = property.name;
  362. titleElement.createTextChild(": ");
  363. }
  364. titleElement.appendChild(this._renderPropertyPreview(property));
  365. }
  366. if (preview.overflow)
  367. titleElement.createChild("span").textContent = "\u2026";
  368. titleElement.createTextChild(isArray ? "]" : "}");
  369. return preview.lossless;
  370. },
  371. /**
  372. * @param {RuntimeAgent.PropertyPreview} property
  373. * @return {Element}
  374. */
  375. _renderPropertyPreview: function(property)
  376. {
  377. var span = document.createElement("span");
  378. span.className = "console-formatted-" + property.type;
  379. if (property.type === "function") {
  380. span.textContent = "function";
  381. return span;
  382. }
  383. if (property.type === "object" && property.subtype === "regexp") {
  384. span.addStyleClass("console-formatted-string");
  385. span.textContent = property.value;
  386. return span;
  387. }
  388. if (property.type === "object" && property.subtype === "node" && property.value) {
  389. span.addStyleClass("console-formatted-preview-node");
  390. WebInspector.DOMPresentationUtils.createSpansForNodeTitle(span, property.value);
  391. return span;
  392. }
  393. if (property.type === "string") {
  394. span.textContent = "\"" + property.value + "\"";
  395. return span;
  396. }
  397. span.textContent = property.value;
  398. return span;
  399. },
  400. _formatParameterAsNode: function(object, elem)
  401. {
  402. function printNode(nodeId)
  403. {
  404. if (!nodeId) {
  405. // Sometimes DOM is loaded after the sync message is being formatted, so we get no
  406. // nodeId here. So we fall back to object formatting here.
  407. this._formatParameterAsObject(object, elem, false);
  408. return;
  409. }
  410. var treeOutline = new WebInspector.ElementsTreeOutline(false, false);
  411. treeOutline.setVisible(true);
  412. treeOutline.rootDOMNode = WebInspector.domAgent.nodeForId(nodeId);
  413. treeOutline.element.addStyleClass("outline-disclosure");
  414. if (!treeOutline.children[0].hasChildren)
  415. treeOutline.element.addStyleClass("single-node");
  416. elem.appendChild(treeOutline.element);
  417. treeOutline.element.treeElementForTest = treeOutline.children[0];
  418. }
  419. object.pushNodeToFrontend(printNode.bind(this));
  420. },
  421. /**
  422. * @param {WebInspector.RemoteObject} array
  423. * @return {boolean}
  424. */
  425. useArrayPreviewInFormatter: function(array)
  426. {
  427. return this.type !== WebInspector.ConsoleMessage.MessageType.DirXML && !!array.preview;
  428. },
  429. /**
  430. * @param {WebInspector.RemoteObject} array
  431. * @param {Element} elem
  432. */
  433. _formatParameterAsArray: function(array, elem)
  434. {
  435. if (this.useArrayPreviewInFormatter(array)) {
  436. this._formatParameterAsArrayOrObject(array, "", elem, true);
  437. return;
  438. }
  439. const maxFlatArrayLength = 100;
  440. if (this._isOutdated || array.arrayLength() > maxFlatArrayLength)
  441. this._formatParameterAsObject(array, elem, false);
  442. else
  443. array.getOwnProperties(this._printArray.bind(this, array, elem));
  444. },
  445. /**
  446. * @param {Array.<WebInspector.RemoteObject>} parameters
  447. * @return {Element}
  448. */
  449. _formatParameterAsTable: function(parameters)
  450. {
  451. var element = document.createElement("span");
  452. var table = parameters[0];
  453. if (!table || !table.preview)
  454. return element;
  455. var columnNames = [];
  456. var preview = table.preview;
  457. var rows = [];
  458. for (var i = 0; i < preview.properties.length; ++i) {
  459. var rowProperty = preview.properties[i];
  460. var rowPreview = rowProperty.valuePreview;
  461. if (!rowPreview)
  462. continue;
  463. var rowValue = {};
  464. const maxColumnsToRender = 20;
  465. for (var j = 0; j < rowPreview.properties.length; ++j) {
  466. var cellProperty = rowPreview.properties[j];
  467. var columnRendered = columnNames.indexOf(cellProperty.name) != -1;
  468. if (!columnRendered) {
  469. if (columnNames.length === maxColumnsToRender)
  470. continue;
  471. columnRendered = true;
  472. columnNames.push(cellProperty.name);
  473. }
  474. if (columnRendered)
  475. rowValue[cellProperty.name] = this._renderPropertyPreview(cellProperty);
  476. }
  477. rows.push([rowProperty.name, rowValue]);
  478. }
  479. var flatValues = [];
  480. for (var i = 0; i < rows.length; ++i) {
  481. var rowName = rows[i][0];
  482. var rowValue = rows[i][1];
  483. flatValues.push(rowName);
  484. for (var j = 0; j < columnNames.length; ++j)
  485. flatValues.push(rowValue[columnNames[j]]);
  486. }
  487. if (!flatValues.length)
  488. return element;
  489. columnNames.unshift(WebInspector.UIString("(index)"));
  490. var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, flatValues);
  491. dataGrid.renderInline();
  492. this._dataGrids.push(dataGrid);
  493. this._dataGridParents.put(dataGrid, element);
  494. return element;
  495. },
  496. _formatParameterAsString: function(output, elem)
  497. {
  498. var span = document.createElement("span");
  499. span.className = "console-formatted-string source-code";
  500. span.appendChild(WebInspector.linkifyStringAsFragment(output.description));
  501. // Make black quotes.
  502. elem.removeStyleClass("console-formatted-string");
  503. elem.appendChild(document.createTextNode("\""));
  504. elem.appendChild(span);
  505. elem.appendChild(document.createTextNode("\""));
  506. },
  507. _printArray: function(array, elem, properties)
  508. {
  509. if (!properties)
  510. return;
  511. var elements = [];
  512. for (var i = 0; i < properties.length; ++i) {
  513. var property = properties[i];
  514. var name = property.name;
  515. if (!isNaN(name))
  516. elements[name] = this._formatAsArrayEntry(property.value);
  517. }
  518. elem.appendChild(document.createTextNode("["));
  519. var lastNonEmptyIndex = -1;
  520. function appendUndefined(elem, index)
  521. {
  522. if (index - lastNonEmptyIndex <= 1)
  523. return;
  524. var span = elem.createChild("span", "console-formatted-undefined");
  525. span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1);
  526. }
  527. var length = array.arrayLength();
  528. for (var i = 0; i < length; ++i) {
  529. var element = elements[i];
  530. if (!element)
  531. continue;
  532. if (i - lastNonEmptyIndex > 1) {
  533. appendUndefined(elem, i);
  534. elem.appendChild(document.createTextNode(", "));
  535. }
  536. elem.appendChild(element);
  537. lastNonEmptyIndex = i;
  538. if (i < length - 1)
  539. elem.appendChild(document.createTextNode(", "));
  540. }
  541. appendUndefined(elem, length);
  542. elem.appendChild(document.createTextNode("]"));
  543. },
  544. _formatAsArrayEntry: function(output)
  545. {
  546. // Prevent infinite expansion of cross-referencing arrays.
  547. return this._formatParameter(output, output.subtype && output.subtype === "array", false);
  548. },
  549. _formatWithSubstitutionString: function(format, parameters, formattedResult)
  550. {
  551. var formatters = {};
  552. function parameterFormatter(force, obj)
  553. {
  554. return this._formatParameter(obj, force, false);
  555. }
  556. function stringFormatter(obj)
  557. {
  558. return obj.description;
  559. }
  560. function floatFormatter(obj)
  561. {
  562. if (typeof obj.value !== "number")
  563. return "NaN";
  564. return obj.value;
  565. }
  566. function integerFormatter(obj)
  567. {
  568. if (typeof obj.value !== "number")
  569. return "NaN";
  570. return Math.floor(obj.value);
  571. }
  572. function bypassFormatter(obj)
  573. {
  574. return (obj instanceof Node) ? obj : "";
  575. }
  576. var currentStyle = null;
  577. function styleFormatter(obj)
  578. {
  579. currentStyle = {};
  580. var buffer = document.createElement("span");
  581. buffer.setAttribute("style", obj.description);
  582. for (var i = 0; i < buffer.style.length; i++) {
  583. var property = buffer.style[i];
  584. if (isWhitelistedProperty(property))
  585. currentStyle[property] = buffer.style[property];
  586. }
  587. }
  588. function isWhitelistedProperty(property)
  589. {
  590. var prefixes = ["background", "border", "color", "font", "line", "margin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font", "-webkit-margin", "-webkit-padding", "-webkit-text"];
  591. for (var i = 0; i < prefixes.length; i++) {
  592. if (property.startsWith(prefixes[i]))
  593. return true;
  594. }
  595. return false;
  596. }
  597. // Firebug uses %o for formatting objects.
  598. formatters.o = parameterFormatter.bind(this, false);
  599. formatters.s = stringFormatter;
  600. formatters.f = floatFormatter;
  601. // Firebug allows both %i and %d for formatting integers.
  602. formatters.i = integerFormatter;
  603. formatters.d = integerFormatter;
  604. // Firebug uses %c for styling the message.
  605. formatters.c = styleFormatter;
  606. // Support %O to force object formatting, instead of the type-based %o formatting.
  607. formatters.O = parameterFormatter.bind(this, true);
  608. formatters._ = bypassFormatter;
  609. function append(a, b)
  610. {
  611. if (b instanceof Node)
  612. a.appendChild(b);
  613. else if (typeof b !== "undefined") {
  614. var toAppend = WebInspector.linkifyStringAsFragment(String(b));
  615. if (currentStyle) {
  616. var wrapper = document.createElement('span');
  617. for (var key in currentStyle)
  618. wrapper.style[key] = currentStyle[key];
  619. wrapper.appendChild(toAppend);
  620. toAppend = wrapper;
  621. }
  622. a.appendChild(toAppend);
  623. }
  624. return a;
  625. }
  626. // String.format does treat formattedResult like a Builder, result is an object.
  627. return String.format(format, parameters, formatters, formattedResult, append);
  628. },
  629. clearHighlight: function()
  630. {
  631. if (!this._formattedMessage)
  632. return;
  633. var highlightedMessage = this._formattedMessage;
  634. delete this._formattedMessage;
  635. delete this._anchorElement;
  636. delete this._messageElement;
  637. this._formatMessage();
  638. this._element.replaceChild(this._formattedMessage, highlightedMessage);
  639. },
  640. highlightSearchResults: function(regexObject)
  641. {
  642. if (!this._formattedMessage)
  643. return;
  644. this._highlightSearchResultsInElement(regexObject, this._messageElement);
  645. if (this._anchorElement)
  646. this._highlightSearchResultsInElement(regexObject, this._anchorElement);
  647. this._element.scrollIntoViewIfNeeded();
  648. },
  649. _highlightSearchResultsInElement: function(regexObject, element)
  650. {
  651. regexObject.lastIndex = 0;
  652. var text = element.textContent;
  653. var match = regexObject.exec(text);
  654. var matchRanges = [];
  655. while (match) {
  656. matchRanges.push({ offset: match.index, length: match[0].length });
  657. match = regexObject.exec(text);
  658. }
  659. WebInspector.highlightSearchResults(element, matchRanges);
  660. },
  661. matchesRegex: function(regexObject)
  662. {
  663. regexObject.lastIndex = 0;
  664. return regexObject.test(this.message) || (this._anchorElement && regexObject.test(this._anchorElement.textContent));
  665. },
  666. toMessageElement: function()
  667. {
  668. if (this._element)
  669. return this._element;
  670. var element = document.createElement("div");
  671. element.message = this;
  672. element.className = "console-message";
  673. this._element = element;
  674. switch (this.level) {
  675. case WebInspector.ConsoleMessage.MessageLevel.Log:
  676. element.addStyleClass("console-log-level");
  677. break;
  678. case WebInspector.ConsoleMessage.MessageLevel.Debug:
  679. element.addStyleClass("console-debug-level");
  680. break;
  681. case WebInspector.ConsoleMessage.MessageLevel.Warning:
  682. element.addStyleClass("console-warning-level");
  683. break;
  684. case WebInspector.ConsoleMessage.MessageLevel.Error:
  685. element.addStyleClass("console-error-level");
  686. break;
  687. }
  688. if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
  689. element.addStyleClass("console-group-title");
  690. element.appendChild(this.formattedMessage);
  691. if (this.repeatCount > 1)
  692. this.updateRepeatCount();
  693. return element;
  694. },
  695. _populateStackTraceTreeElement: function(parentTreeElement)
  696. {
  697. for (var i = 0; i < this._stackTrace.length; i++) {
  698. var frame = this._stackTrace[i];
  699. var content = document.createElement("div");
  700. var messageTextElement = document.createElement("span");
  701. messageTextElement.className = "console-message-text source-code";
  702. var functionName = frame.functionName || WebInspector.UIString("(anonymous function)");
  703. messageTextElement.appendChild(document.createTextNode(functionName));
  704. content.appendChild(messageTextElement);
  705. if (frame.scriptId) {
  706. content.appendChild(document.createTextNode(" "));
  707. var urlElement = this._linkifyCallFrame(frame);
  708. if (!urlElement)
  709. continue;
  710. content.appendChild(urlElement);
  711. }
  712. var treeElement = new TreeElement(content);
  713. parentTreeElement.appendChild(treeElement);
  714. }
  715. },
  716. updateRepeatCount: function() {
  717. if (!this._element)
  718. return;
  719. if (!this.repeatCountElement) {
  720. this.repeatCountElement = document.createElement("span");
  721. this.repeatCountElement.className = "bubble";
  722. this._element.insertBefore(this.repeatCountElement, this._element.firstChild);
  723. this._element.addStyleClass("repeated-message");
  724. }
  725. this.repeatCountElement.textContent = this.repeatCount;
  726. },
  727. toString: function()
  728. {
  729. var sourceString;
  730. switch (this.source) {
  731. case WebInspector.ConsoleMessage.MessageSource.XML:
  732. sourceString = "XML";
  733. break;
  734. case WebInspector.ConsoleMessage.MessageSource.JS:
  735. sourceString = "JS";
  736. break;
  737. case WebInspector.ConsoleMessage.MessageSource.Network:
  738. sourceString = "Network";
  739. break;
  740. case WebInspector.ConsoleMessage.MessageSource.ConsoleAPI:
  741. sourceString = "ConsoleAPI";
  742. break;
  743. case WebInspector.ConsoleMessage.MessageSource.Storage:
  744. sourceString = "Storage";
  745. break;
  746. case WebInspector.ConsoleMessage.MessageSource.AppCache:
  747. sourceString = "AppCache";
  748. break;
  749. case WebInspector.ConsoleMessage.MessageSource.Rendering:
  750. sourceString = "Rendering";
  751. break;
  752. case WebInspector.ConsoleMessage.MessageSource.CSS:
  753. sourceString = "CSS";
  754. break;
  755. case WebInspector.ConsoleMessage.MessageSource.Security:
  756. sourceString = "Security";
  757. break;
  758. case WebInspector.ConsoleMessage.MessageSource.Other:
  759. sourceString = "Other";
  760. break;
  761. }
  762. var typeString;
  763. switch (this.type) {
  764. case WebInspector.ConsoleMessage.MessageType.Log:
  765. typeString = "Log";
  766. break;
  767. case WebInspector.ConsoleMessage.MessageType.Dir:
  768. typeString = "Dir";
  769. break;
  770. case WebInspector.ConsoleMessage.MessageType.DirXML:
  771. typeString = "Dir XML";
  772. break;
  773. case WebInspector.ConsoleMessage.MessageType.Trace:
  774. typeString = "Trace";
  775. break;
  776. case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
  777. case WebInspector.ConsoleMessage.MessageType.StartGroup:
  778. typeString = "Start Group";
  779. break;
  780. case WebInspector.ConsoleMessage.MessageType.EndGroup:
  781. typeString = "End Group";
  782. break;
  783. case WebInspector.ConsoleMessage.MessageType.Assert:
  784. typeString = "Assert";
  785. break;
  786. case WebInspector.ConsoleMessage.MessageType.Result:
  787. typeString = "Result";
  788. break;
  789. case WebInspector.ConsoleMessage.MessageType.Profile:
  790. case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
  791. typeString = "Profiling";
  792. break;
  793. }
  794. var levelString;
  795. switch (this.level) {
  796. case WebInspector.ConsoleMessage.MessageLevel.Log:
  797. levelString = "Log";
  798. break;
  799. case WebInspector.ConsoleMessage.MessageLevel.Warning:
  800. levelString = "Warning";
  801. break;
  802. case WebInspector.ConsoleMessage.MessageLevel.Debug:
  803. levelString = "Debug";
  804. break;
  805. case WebInspector.ConsoleMessage.MessageLevel.Error:
  806. levelString = "Error";
  807. break;
  808. }
  809. return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage.textContent + "\n" + this.url + " line " + this.line;
  810. },
  811. get text()
  812. {
  813. return this._messageText;
  814. },
  815. location: function()
  816. {
  817. // FIXME(62725): stack trace line/column numbers are one-based.
  818. var lineNumber = this.stackTrace ? this.stackTrace[0].lineNumber - 1 : this.line - 1;
  819. var columnNumber = this.stackTrace && this.stackTrace[0].columnNumber ? this.stackTrace[0].columnNumber - 1 : 0;
  820. return WebInspector.debuggerModel.createRawLocationByURL(this.url, lineNumber, columnNumber);
  821. },
  822. isEqual: function(msg)
  823. {
  824. if (!msg)
  825. return false;
  826. if (this._stackTrace) {
  827. if (!msg._stackTrace)
  828. return false;
  829. var l = this._stackTrace;
  830. var r = msg._stackTrace;
  831. if (l.length !== r.length)
  832. return false;
  833. for (var i = 0; i < l.length; i++) {
  834. if (l[i].url !== r[i].url ||
  835. l[i].functionName !== r[i].functionName ||
  836. l[i].lineNumber !== r[i].lineNumber ||
  837. l[i].columnNumber !== r[i].columnNumber)
  838. return false;
  839. }
  840. }
  841. return (this.source === msg.source)
  842. && (this.type === msg.type)
  843. && (this.level === msg.level)
  844. && (this.line === msg.line)
  845. && (this.url === msg.url)
  846. && (this.message === msg.message)
  847. && (this._request === msg._request);
  848. },
  849. get stackTrace()
  850. {
  851. return this._stackTrace;
  852. },
  853. /**
  854. * @return {WebInspector.ConsoleMessage}
  855. */
  856. clone: function()
  857. {
  858. return WebInspector.ConsoleMessage.create(this.source, this.level, this._messageText, this.type, this.url, this.line, this.column, this.repeatCount, this._parameters, this._stackTrace, this._request ? this._request.requestId : undefined, this._isOutdated);
  859. },
  860. __proto__: WebInspector.ConsoleMessage.prototype
  861. }