OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | 3 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
4 * Copyright (C) 2009 Joseph Pecoraro | 4 * Copyright (C) 2009 Joseph Pecoraro |
5 * | 5 * |
6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
8 * are met: | 8 * are met: |
9 * | 9 * |
10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
11 * notice, this list of conditions and the following disclaimer. | 11 * notice, this list of conditions and the following disclaimer. |
12 * 2. Redistributions in binary form must reproduce the above copyright | 12 * 2. Redistributions in binary form must reproduce the above copyright |
13 * notice, this list of conditions and the following disclaimer in the | 13 * notice, this list of conditions and the following disclaimer in the |
14 * documentation and/or other materials provided with the distribution. | 14 * documentation and/or other materials provided with the distribution. |
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 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 | 16 * its contributors may be used to endorse or promote products derived |
17 * from this software without specific prior written permission. | 17 * from this software without specific prior written permission. |
18 * | 18 * |
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 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 | 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 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 | 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 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 | 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 | 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 | 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. | 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | |
33 * @implements {WebInspector.ViewportElement} | 31 * @implements {WebInspector.ViewportElement} |
34 * @param {!WebInspector.ConsoleMessage} consoleMessage | 32 * @unrestricted |
35 * @param {!WebInspector.Linkifier} linkifier | |
36 * @param {number} nestingLevel | |
37 */ | 33 */ |
38 WebInspector.ConsoleViewMessage = function(consoleMessage, linkifier, nestingLev
el) | 34 WebInspector.ConsoleViewMessage = class { |
39 { | 35 /** |
| 36 * @param {!WebInspector.ConsoleMessage} consoleMessage |
| 37 * @param {!WebInspector.Linkifier} linkifier |
| 38 * @param {number} nestingLevel |
| 39 */ |
| 40 constructor(consoleMessage, linkifier, nestingLevel) { |
40 this._message = consoleMessage; | 41 this._message = consoleMessage; |
41 this._linkifier = linkifier; | 42 this._linkifier = linkifier; |
42 this._repeatCount = 1; | 43 this._repeatCount = 1; |
43 this._closeGroupDecorationCount = 0; | 44 this._closeGroupDecorationCount = 0; |
44 this._nestingLevel = nestingLevel; | 45 this._nestingLevel = nestingLevel; |
45 | 46 |
46 /** @type {?WebInspector.DataGrid} */ | 47 /** @type {?WebInspector.DataGrid} */ |
47 this._dataGrid = null; | 48 this._dataGrid = null; |
48 this._previewFormatter = new WebInspector.RemoteObjectPreviewFormatter(); | 49 this._previewFormatter = new WebInspector.RemoteObjectPreviewFormatter(); |
49 this._searchRegex = null; | 50 this._searchRegex = null; |
50 }; | 51 } |
51 | 52 |
52 WebInspector.ConsoleViewMessage.prototype = { | 53 /** |
| 54 * @return {?WebInspector.Target} |
| 55 */ |
| 56 _target() { |
| 57 return this.consoleMessage().target(); |
| 58 } |
| 59 |
| 60 /** |
| 61 * @override |
| 62 * @return {!Element} |
| 63 */ |
| 64 element() { |
| 65 return this.toMessageElement(); |
| 66 } |
| 67 |
| 68 /** |
| 69 * @override |
| 70 */ |
| 71 wasShown() { |
| 72 if (this._dataGrid) |
| 73 this._dataGrid.updateWidths(); |
| 74 this._isVisible = true; |
| 75 } |
| 76 |
| 77 onResize() { |
| 78 if (!this._isVisible) |
| 79 return; |
| 80 if (this._dataGrid) |
| 81 this._dataGrid.onResize(); |
| 82 } |
| 83 |
| 84 /** |
| 85 * @override |
| 86 */ |
| 87 willHide() { |
| 88 this._isVisible = false; |
| 89 this._cachedHeight = this.contentElement().offsetHeight; |
| 90 } |
| 91 |
| 92 /** |
| 93 * @return {number} |
| 94 */ |
| 95 fastHeight() { |
| 96 if (this._cachedHeight) |
| 97 return this._cachedHeight; |
| 98 // This value reflects the 18px min-height of .console-message, plus the |
| 99 // 1px border of .console-message-wrapper. Keep in sync with consoleView.css
. |
| 100 const defaultConsoleRowHeight = 19; |
| 101 if (this._message.type === WebInspector.ConsoleMessage.MessageType.Table) { |
| 102 var table = this._message.parameters[0]; |
| 103 if (table && table.preview) |
| 104 return defaultConsoleRowHeight * table.preview.properties.length; |
| 105 } |
| 106 return defaultConsoleRowHeight; |
| 107 } |
| 108 |
| 109 /** |
| 110 * @return {!WebInspector.ConsoleMessage} |
| 111 */ |
| 112 consoleMessage() { |
| 113 return this._message; |
| 114 } |
| 115 |
| 116 /** |
| 117 * @param {!WebInspector.ConsoleMessage} consoleMessage |
| 118 * @return {!Element} |
| 119 */ |
| 120 _buildTableMessage(consoleMessage) { |
| 121 var formattedMessage = createElement('span'); |
| 122 WebInspector.appendStyle(formattedMessage, 'components/objectValue.css'); |
| 123 formattedMessage.className = 'console-message-text source-code'; |
| 124 var anchorElement = this._buildMessageAnchor(consoleMessage); |
| 125 if (anchorElement) |
| 126 formattedMessage.appendChild(anchorElement); |
| 127 |
| 128 var table = consoleMessage.parameters && consoleMessage.parameters.length ?
consoleMessage.parameters[0] : null; |
| 129 if (table) |
| 130 table = this._parameterToRemoteObject(table, this._target()); |
| 131 if (!table || !table.preview) |
| 132 return formattedMessage; |
| 133 |
| 134 var columnNames = []; |
| 135 var preview = table.preview; |
| 136 var rows = []; |
| 137 for (var i = 0; i < preview.properties.length; ++i) { |
| 138 var rowProperty = preview.properties[i]; |
| 139 var rowPreview = rowProperty.valuePreview; |
| 140 if (!rowPreview) |
| 141 continue; |
| 142 |
| 143 var rowValue = {}; |
| 144 const maxColumnsToRender = 20; |
| 145 for (var j = 0; j < rowPreview.properties.length; ++j) { |
| 146 var cellProperty = rowPreview.properties[j]; |
| 147 var columnRendered = columnNames.indexOf(cellProperty.name) !== -1; |
| 148 if (!columnRendered) { |
| 149 if (columnNames.length === maxColumnsToRender) |
| 150 continue; |
| 151 columnRendered = true; |
| 152 columnNames.push(cellProperty.name); |
| 153 } |
| 154 |
| 155 if (columnRendered) { |
| 156 var cellElement = this._renderPropertyPreviewOrAccessor(table, [rowPro
perty, cellProperty]); |
| 157 cellElement.classList.add('console-message-nowrap-below'); |
| 158 rowValue[cellProperty.name] = cellElement; |
| 159 } |
| 160 } |
| 161 rows.push([rowProperty.name, rowValue]); |
| 162 } |
| 163 |
| 164 var flatValues = []; |
| 165 for (var i = 0; i < rows.length; ++i) { |
| 166 var rowName = rows[i][0]; |
| 167 var rowValue = rows[i][1]; |
| 168 flatValues.push(rowName); |
| 169 for (var j = 0; j < columnNames.length; ++j) |
| 170 flatValues.push(rowValue[columnNames[j]]); |
| 171 } |
| 172 columnNames.unshift(WebInspector.UIString('(index)')); |
| 173 |
| 174 if (flatValues.length) { |
| 175 this._dataGrid = WebInspector.SortableDataGrid.create(columnNames, flatVal
ues); |
| 176 |
| 177 var formattedResult = createElement('span'); |
| 178 var tableElement = formattedResult.createChild('div', 'console-message-for
matted-table'); |
| 179 var dataGridContainer = tableElement.createChild('span'); |
| 180 tableElement.appendChild(this._formatParameter(table, true, false)); |
| 181 dataGridContainer.appendChild(this._dataGrid.element); |
| 182 formattedMessage.appendChild(formattedResult); |
| 183 this._dataGrid.renderInline(); |
| 184 } |
| 185 return formattedMessage; |
| 186 } |
| 187 |
| 188 /** |
| 189 * @param {!WebInspector.ConsoleMessage} consoleMessage |
| 190 * @return {!Element} |
| 191 */ |
| 192 _buildMessage(consoleMessage) { |
| 193 var messageElement; |
| 194 if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.Cons
oleAPI) { |
| 195 switch (consoleMessage.type) { |
| 196 case WebInspector.ConsoleMessage.MessageType.Trace: |
| 197 messageElement = this._format(consoleMessage.parameters || ['console.t
race']); |
| 198 break; |
| 199 case WebInspector.ConsoleMessage.MessageType.Clear: |
| 200 messageElement = createElementWithClass('span', 'console-info'); |
| 201 messageElement.textContent = WebInspector.UIString('Console was cleare
d'); |
| 202 break; |
| 203 case WebInspector.ConsoleMessage.MessageType.Assert: |
| 204 var args = [WebInspector.UIString('Assertion failed:')]; |
| 205 if (consoleMessage.parameters) |
| 206 args = args.concat(consoleMessage.parameters); |
| 207 messageElement = this._format(args); |
| 208 break; |
| 209 case WebInspector.ConsoleMessage.MessageType.Dir: |
| 210 var obj = consoleMessage.parameters ? consoleMessage.parameters[0] : u
ndefined; |
| 211 var args = ['%O', obj]; |
| 212 messageElement = this._format(args); |
| 213 break; |
| 214 case WebInspector.ConsoleMessage.MessageType.Profile: |
| 215 case WebInspector.ConsoleMessage.MessageType.ProfileEnd: |
| 216 messageElement = this._format([consoleMessage.messageText]); |
| 217 break; |
| 218 default: |
| 219 if (consoleMessage.parameters && consoleMessage.parameters.length ===
1 && |
| 220 consoleMessage.parameters[0].type === 'string') |
| 221 messageElement = this._tryFormatAsError(/** @type {string} */ (conso
leMessage.parameters[0].value)); |
| 222 var args = consoleMessage.parameters || [consoleMessage.messageText]; |
| 223 messageElement = messageElement || this._format(args); |
| 224 } |
| 225 } else if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSour
ce.Network) { |
| 226 if (consoleMessage.request) { |
| 227 messageElement = createElement('span'); |
| 228 if (consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Er
ror || |
| 229 consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Re
vokedError) { |
| 230 messageElement.createTextChildren(consoleMessage.request.requestMethod
, ' '); |
| 231 messageElement.appendChild(WebInspector.Linkifier.linkifyUsingRevealer
( |
| 232 consoleMessage.request, consoleMessage.request.url, consoleMessage
.request.url)); |
| 233 if (consoleMessage.request.failed) |
| 234 messageElement.createTextChildren(' ', consoleMessage.request.locali
zedFailDescription); |
| 235 else |
| 236 messageElement.createTextChildren( |
| 237 ' ', String(consoleMessage.request.statusCode), ' (', consoleMes
sage.request.statusText, ')'); |
| 238 } else { |
| 239 var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier
( |
| 240 consoleMessage.messageText, linkifyRequest.bind(consoleMessage)); |
| 241 messageElement.appendChild(fragment); |
| 242 } |
| 243 } else { |
| 244 messageElement = this._format([consoleMessage.messageText]); |
| 245 } |
| 246 } else { |
| 247 var args = consoleMessage.parameters || [consoleMessage.messageText]; |
| 248 messageElement = this._format(args); |
| 249 } |
| 250 |
| 251 var formattedMessage = createElement('span'); |
| 252 WebInspector.appendStyle(formattedMessage, 'components/objectValue.css'); |
| 253 formattedMessage.className = 'console-message-text source-code'; |
| 254 |
| 255 var anchorElement = this._buildMessageAnchor(consoleMessage); |
| 256 if (anchorElement) |
| 257 formattedMessage.appendChild(anchorElement); |
| 258 formattedMessage.appendChild(messageElement); |
| 259 return formattedMessage; |
| 260 |
53 /** | 261 /** |
54 * @return {?WebInspector.Target} | 262 * @param {string} title |
| 263 * @return {!Element} |
| 264 * @this {WebInspector.ConsoleMessage} |
55 */ | 265 */ |
56 _target: function() | 266 function linkifyRequest(title) { |
57 { | 267 return WebInspector.Linkifier.linkifyUsingRevealer( |
58 return this.consoleMessage().target(); | 268 /** @type {!WebInspector.NetworkRequest} */ (this.request), title, thi
s.request.url); |
59 }, | 269 } |
| 270 } |
| 271 |
| 272 /** |
| 273 * @param {!WebInspector.ConsoleMessage} consoleMessage |
| 274 * @return {?Element} |
| 275 */ |
| 276 _buildMessageAnchor(consoleMessage) { |
| 277 var anchorElement = null; |
| 278 if (consoleMessage.source !== WebInspector.ConsoleMessage.MessageSource.Netw
ork || consoleMessage.request) { |
| 279 if (consoleMessage.scriptId) |
| 280 anchorElement = this._linkifyScriptId( |
| 281 consoleMessage.scriptId, consoleMessage.url || '', consoleMessage.li
ne, consoleMessage.column); |
| 282 else if (consoleMessage.stackTrace && consoleMessage.stackTrace.callFrames
.length) |
| 283 anchorElement = this._linkifyStackTraceTopFrame(consoleMessage.stackTrac
e); |
| 284 else if (consoleMessage.url && consoleMessage.url !== 'undefined') |
| 285 anchorElement = this._linkifyLocation(consoleMessage.url, consoleMessage
.line, consoleMessage.column); |
| 286 } else if (consoleMessage.url) { |
| 287 var url = consoleMessage.url; |
| 288 var isExternal = |
| 289 !WebInspector.resourceForURL(url) && !WebInspector.networkMapping.uiSo
urceCodeForURLForAnyTarget(url); |
| 290 anchorElement = WebInspector.linkifyURLAsNode(url, url, 'console-message-u
rl', isExternal); |
| 291 } |
| 292 |
| 293 // Append a space to prevent the anchor text from being glued to the console
message when the user selects and copies the console messages. |
| 294 if (anchorElement) |
| 295 anchorElement.appendChild(createTextNode(' ')); |
| 296 return anchorElement; |
| 297 } |
| 298 |
| 299 /** |
| 300 * @param {!WebInspector.ConsoleMessage} consoleMessage |
| 301 * @param {!WebInspector.Target} target |
| 302 * @param {!WebInspector.Linkifier} linkifier |
| 303 * @return {!Element} |
| 304 */ |
| 305 _buildMessageWithStackTrace(consoleMessage, target, linkifier) { |
| 306 var toggleElement = createElementWithClass('div', 'console-message-stack-tra
ce-toggle'); |
| 307 var triangleElement = toggleElement.createChild('div', 'console-message-stac
k-trace-triangle'); |
| 308 var contentElement = toggleElement.createChild('div', 'console-message-stack
-trace-wrapper'); |
| 309 |
| 310 var messageElement = this._buildMessage(consoleMessage); |
| 311 var clickableElement = contentElement.createChild('div'); |
| 312 clickableElement.appendChild(messageElement); |
| 313 var stackTraceElement = contentElement.createChild('div'); |
| 314 var stackTracePreview = |
| 315 WebInspector.DOMPresentationUtils.buildStackTracePreviewContents(target,
linkifier, consoleMessage.stackTrace); |
| 316 stackTraceElement.appendChild(stackTracePreview); |
| 317 stackTraceElement.classList.add('hidden'); |
60 | 318 |
61 /** | 319 /** |
62 * @override | 320 * @param {boolean} expand |
63 * @return {!Element} | |
64 */ | 321 */ |
65 element: function() | 322 function expandStackTrace(expand) { |
66 { | 323 stackTraceElement.classList.toggle('hidden', !expand); |
67 return this.toMessageElement(); | 324 toggleElement.classList.toggle('expanded', expand); |
68 }, | 325 } |
69 | 326 |
70 /** | 327 /** |
71 * @override | 328 * @param {?Event} event |
72 */ | 329 */ |
73 wasShown: function() | 330 function toggleStackTrace(event) { |
74 { | 331 if (event.target.hasSelection()) |
75 if (this._dataGrid) | 332 return; |
76 this._dataGrid.updateWidths(); | 333 expandStackTrace(stackTraceElement.classList.contains('hidden')); |
77 this._isVisible = true; | 334 event.consume(); |
78 }, | 335 } |
79 | 336 |
80 onResize: function() | 337 clickableElement.addEventListener('click', toggleStackTrace, false); |
81 { | 338 triangleElement.addEventListener('click', toggleStackTrace, false); |
82 if (!this._isVisible) | 339 if (consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trace) |
83 return; | 340 expandStackTrace(true); |
84 if (this._dataGrid) | 341 |
85 this._dataGrid.onResize(); | 342 toggleElement._expandStackTraceForTest = expandStackTrace.bind(null, true); |
86 }, | 343 return toggleElement; |
| 344 } |
| 345 |
| 346 /** |
| 347 * @param {string} url |
| 348 * @param {number} lineNumber |
| 349 * @param {number} columnNumber |
| 350 * @return {?Element} |
| 351 */ |
| 352 _linkifyLocation(url, lineNumber, columnNumber) { |
| 353 var target = this._target(); |
| 354 if (!target) |
| 355 return null; |
| 356 return this._linkifier.linkifyScriptLocation(target, null, url, lineNumber,
columnNumber, 'console-message-url'); |
| 357 } |
| 358 |
| 359 /** |
| 360 * @param {!RuntimeAgent.StackTrace} stackTrace |
| 361 * @return {?Element} |
| 362 */ |
| 363 _linkifyStackTraceTopFrame(stackTrace) { |
| 364 var target = this._target(); |
| 365 if (!target) |
| 366 return null; |
| 367 return this._linkifier.linkifyStackTraceTopFrame(target, stackTrace, 'consol
e-message-url'); |
| 368 } |
| 369 |
| 370 /** |
| 371 * @param {string} scriptId |
| 372 * @param {string} url |
| 373 * @param {number} lineNumber |
| 374 * @param {number} columnNumber |
| 375 * @return {?Element} |
| 376 */ |
| 377 _linkifyScriptId(scriptId, url, lineNumber, columnNumber) { |
| 378 var target = this._target(); |
| 379 if (!target) |
| 380 return null; |
| 381 return this._linkifier.linkifyScriptLocation( |
| 382 target, scriptId, url, lineNumber, columnNumber, 'console-message-url'); |
| 383 } |
| 384 |
| 385 /** |
| 386 * @param {!WebInspector.RemoteObject|!Object|string} parameter |
| 387 * @param {?WebInspector.Target} target |
| 388 * @return {!WebInspector.RemoteObject} |
| 389 */ |
| 390 _parameterToRemoteObject(parameter, target) { |
| 391 if (parameter instanceof WebInspector.RemoteObject) |
| 392 return parameter; |
| 393 if (!target) |
| 394 return WebInspector.RemoteObject.fromLocalObject(parameter); |
| 395 if (typeof parameter === 'object') |
| 396 return target.runtimeModel.createRemoteObject(parameter); |
| 397 return target.runtimeModel.createRemoteObjectFromPrimitiveValue(parameter); |
| 398 } |
| 399 |
| 400 /** |
| 401 * @param {!Array.<!WebInspector.RemoteObject|string>} parameters |
| 402 * @return {!Element} |
| 403 */ |
| 404 _format(parameters) { |
| 405 // This node is used like a Builder. Values are continually appended onto it
. |
| 406 var formattedResult = createElement('span'); |
| 407 if (!parameters.length) |
| 408 return formattedResult; |
| 409 |
| 410 // Formatting code below assumes that parameters are all wrappers whereas fr
ontend console |
| 411 // API allows passing arbitrary values as messages (strings, numbers, etc.).
Wrap them here. |
| 412 // FIXME: Only pass runtime wrappers here. |
| 413 for (var i = 0; i < parameters.length; ++i) |
| 414 parameters[i] = this._parameterToRemoteObject(parameters[i], this._target(
)); |
| 415 |
| 416 // There can be string log and string eval result. We distinguish between th
em based on message type. |
| 417 var shouldFormatMessage = WebInspector.RemoteObject.type( |
| 418 (/** @type {!Array.<!WebInspector.RemoteObject
>} **/ (parameters))[0]) === 'string' && |
| 419 (this._message.type !== WebInspector.ConsoleMessage.MessageType.Result |
| |
| 420 this._message.level === WebInspector.ConsoleMessage.MessageLevel.Error
|| |
| 421 this._message.level === WebInspector.ConsoleMessage.MessageLevel.Revoke
dError); |
| 422 |
| 423 // Multiple parameters with the first being a format string. Save unused sub
stitutions. |
| 424 if (shouldFormatMessage) { |
| 425 var result = this._formatWithSubstitutionString( |
| 426 /** @type {string} **/ (parameters[0].description), parameters.slice(1
), formattedResult); |
| 427 parameters = result.unusedSubstitutions; |
| 428 if (parameters.length) |
| 429 formattedResult.createTextChild(' '); |
| 430 } |
| 431 |
| 432 // Single parameter, or unused substitutions from above. |
| 433 for (var i = 0; i < parameters.length; ++i) { |
| 434 // Inline strings when formatting. |
| 435 if (shouldFormatMessage && parameters[i].type === 'string') |
| 436 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(paramet
ers[i].description)); |
| 437 else |
| 438 formattedResult.appendChild(this._formatParameter(parameters[i], false,
true)); |
| 439 if (i < parameters.length - 1) |
| 440 formattedResult.createTextChild(' '); |
| 441 } |
| 442 return formattedResult; |
| 443 } |
| 444 |
| 445 /** |
| 446 * @param {!WebInspector.RemoteObject} output |
| 447 * @param {boolean=} forceObjectFormat |
| 448 * @param {boolean=} includePreview |
| 449 * @return {!Element} |
| 450 */ |
| 451 _formatParameter(output, forceObjectFormat, includePreview) { |
| 452 if (output.customPreview()) |
| 453 return (new WebInspector.CustomPreviewComponent(output)).element; |
| 454 |
| 455 var type = forceObjectFormat ? 'object' : (output.subtype || output.type); |
| 456 var element; |
| 457 switch (type) { |
| 458 case 'array': |
| 459 case 'typedarray': |
| 460 element = this._formatParameterAsArray(output); |
| 461 break; |
| 462 case 'error': |
| 463 element = this._formatParameterAsError(output); |
| 464 break; |
| 465 case 'function': |
| 466 case 'generator': |
| 467 element = this._formatParameterAsFunction(output, includePreview); |
| 468 break; |
| 469 case 'iterator': |
| 470 case 'map': |
| 471 case 'object': |
| 472 case 'promise': |
| 473 case 'proxy': |
| 474 case 'set': |
| 475 element = this._formatParameterAsObject(output, includePreview); |
| 476 break; |
| 477 case 'node': |
| 478 element = this._formatParameterAsNode(output); |
| 479 break; |
| 480 case 'string': |
| 481 element = this._formatParameterAsString(output); |
| 482 break; |
| 483 case 'boolean': |
| 484 case 'date': |
| 485 case 'null': |
| 486 case 'number': |
| 487 case 'regexp': |
| 488 case 'symbol': |
| 489 case 'undefined': |
| 490 element = this._formatParameterAsValue(output); |
| 491 break; |
| 492 default: |
| 493 element = this._formatParameterAsValue(output); |
| 494 console.error('Tried to format remote object of unknown type.'); |
| 495 } |
| 496 element.classList.add('object-value-' + type); |
| 497 element.classList.add('source-code'); |
| 498 return element; |
| 499 } |
| 500 |
| 501 /** |
| 502 * @param {!WebInspector.RemoteObject} obj |
| 503 * @return {!Element} |
| 504 */ |
| 505 _formatParameterAsValue(obj) { |
| 506 var result = createElement('span'); |
| 507 result.createTextChild(obj.description || ''); |
| 508 if (obj.objectId) |
| 509 result.addEventListener('contextmenu', this._contextMenuEventFired.bind(th
is, obj), false); |
| 510 return result; |
| 511 } |
| 512 |
| 513 /** |
| 514 * @param {!WebInspector.RemoteObject} obj |
| 515 * @param {boolean=} includePreview |
| 516 * @return {!Element} |
| 517 */ |
| 518 _formatParameterAsObject(obj, includePreview) { |
| 519 var titleElement = createElement('span'); |
| 520 if (includePreview && obj.preview) { |
| 521 titleElement.classList.add('console-object-preview'); |
| 522 this._previewFormatter.appendObjectPreview(titleElement, obj.preview); |
| 523 } else if (obj.type === 'function') { |
| 524 WebInspector.ObjectPropertiesSection.formatObjectAsFunction(obj, titleElem
ent, false); |
| 525 titleElement.classList.add('object-value-function'); |
| 526 } else { |
| 527 titleElement.createTextChild(obj.description || ''); |
| 528 } |
| 529 |
| 530 var section = new WebInspector.ObjectPropertiesSection(obj, titleElement, th
is._linkifier); |
| 531 section.element.classList.add('console-view-object-properties-section'); |
| 532 section.enableContextMenu(); |
| 533 return section.element; |
| 534 } |
| 535 |
| 536 /** |
| 537 * @param {!WebInspector.RemoteObject} func |
| 538 * @param {boolean=} includePreview |
| 539 * @return {!Element} |
| 540 */ |
| 541 _formatParameterAsFunction(func, includePreview) { |
| 542 var result = createElement('span'); |
| 543 WebInspector.RemoteFunction.objectAsFunction(func).targetFunction().then(for
matTargetFunction.bind(this)); |
| 544 return result; |
87 | 545 |
88 /** | 546 /** |
89 * @override | 547 * @param {!WebInspector.RemoteObject} targetFunction |
| 548 * @this {WebInspector.ConsoleViewMessage} |
90 */ | 549 */ |
91 willHide: function() | 550 function formatTargetFunction(targetFunction) { |
92 { | 551 var functionElement = createElement('span'); |
93 this._isVisible = false; | 552 WebInspector.ObjectPropertiesSection.formatObjectAsFunction( |
94 this._cachedHeight = this.contentElement().offsetHeight; | 553 targetFunction, functionElement, true, includePreview); |
95 }, | 554 result.appendChild(functionElement); |
| 555 if (targetFunction !== func) { |
| 556 var note = result.createChild('span', 'object-info-state-note'); |
| 557 note.title = WebInspector.UIString('Function was resolved from bound fun
ction.'); |
| 558 } |
| 559 result.addEventListener('contextmenu', this._contextMenuEventFired.bind(th
is, targetFunction), false); |
| 560 } |
| 561 } |
| 562 |
| 563 /** |
| 564 * @param {!WebInspector.RemoteObject} obj |
| 565 * @param {!Event} event |
| 566 */ |
| 567 _contextMenuEventFired(obj, event) { |
| 568 var contextMenu = new WebInspector.ContextMenu(event); |
| 569 contextMenu.appendApplicableItems(obj); |
| 570 contextMenu.show(); |
| 571 } |
| 572 |
| 573 /** |
| 574 * @param {?WebInspector.RemoteObject} object |
| 575 * @param {!Array.<!RuntimeAgent.PropertyPreview>} propertyPath |
| 576 * @return {!Element} |
| 577 */ |
| 578 _renderPropertyPreviewOrAccessor(object, propertyPath) { |
| 579 var property = propertyPath.peekLast(); |
| 580 if (property.type === 'accessor') |
| 581 return this._formatAsAccessorProperty(object, propertyPath.map(property =>
property.name), false); |
| 582 return this._previewFormatter.renderPropertyPreview( |
| 583 property.type, /** @type {string} */ (property.subtype), property.value)
; |
| 584 } |
| 585 |
| 586 /** |
| 587 * @param {!WebInspector.RemoteObject} object |
| 588 * @return {!Element} |
| 589 */ |
| 590 _formatParameterAsNode(object) { |
| 591 var result = createElement('span'); |
| 592 WebInspector.Renderer.renderPromise(object).then(appendRenderer.bind(this),
failedToRender.bind(this)); |
| 593 return result; |
96 | 594 |
97 /** | 595 /** |
98 * @return {number} | 596 * @param {!Element} rendererElement |
| 597 * @this {WebInspector.ConsoleViewMessage} |
99 */ | 598 */ |
100 fastHeight: function() | 599 function appendRenderer(rendererElement) { |
101 { | 600 result.appendChild(rendererElement); |
102 if (this._cachedHeight) | 601 this._formattedParameterAsNodeForTest(); |
103 return this._cachedHeight; | 602 } |
104 // This value reflects the 18px min-height of .console-message, plus the | 603 |
105 // 1px border of .console-message-wrapper. Keep in sync with consoleView
.css. | 604 /** |
106 const defaultConsoleRowHeight = 19; | 605 * @this {WebInspector.ConsoleViewMessage} |
107 if (this._message.type === WebInspector.ConsoleMessage.MessageType.Table
) { | 606 */ |
108 var table = this._message.parameters[0]; | 607 function failedToRender() { |
109 if (table && table.preview) | 608 result.appendChild(this._formatParameterAsObject(object, false)); |
110 return defaultConsoleRowHeight * table.preview.properties.length
; | 609 } |
| 610 } |
| 611 |
| 612 _formattedParameterAsNodeForTest() { |
| 613 } |
| 614 |
| 615 /** |
| 616 * @param {!WebInspector.RemoteObject} array |
| 617 * @return {!Element} |
| 618 */ |
| 619 _formatParameterAsArray(array) { |
| 620 var usePrintedArrayFormat = this._message.type !== WebInspector.ConsoleMessa
ge.MessageType.DirXML && |
| 621 this._message.type !== WebInspector.ConsoleMessage.MessageType.Result; |
| 622 var isLongArray = array.arrayLength() > 100; |
| 623 if (usePrintedArrayFormat || isLongArray) |
| 624 return this._formatParameterAsObject(array, usePrintedArrayFormat || !isLo
ngArray); |
| 625 var result = createElement('span'); |
| 626 array.getAllProperties(false, printArrayResult.bind(this)); |
| 627 return result; |
| 628 |
| 629 /** |
| 630 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties |
| 631 * @this {!WebInspector.ConsoleViewMessage} |
| 632 */ |
| 633 function printArrayResult(properties) { |
| 634 if (!properties) { |
| 635 result.appendChild(this._formatParameterAsObject(array, false)); |
| 636 return; |
| 637 } |
| 638 |
| 639 var titleElement = createElement('span'); |
| 640 var elements = {}; |
| 641 for (var i = 0; i < properties.length; ++i) { |
| 642 var property = properties[i]; |
| 643 var name = property.name; |
| 644 if (isNaN(name)) |
| 645 continue; |
| 646 if (property.getter) |
| 647 elements[name] = this._formatAsAccessorProperty(array, [name], true); |
| 648 else if (property.value) |
| 649 elements[name] = this._formatAsArrayEntry(property.value); |
| 650 } |
| 651 |
| 652 titleElement.createTextChild('['); |
| 653 var lastNonEmptyIndex = -1; |
| 654 |
| 655 function appendUndefined(titleElement, index) { |
| 656 if (index - lastNonEmptyIndex <= 1) |
| 657 return; |
| 658 var span = titleElement.createChild('span', 'object-value-undefined'); |
| 659 span.textContent = WebInspector.UIString('undefined × %d', index - lastN
onEmptyIndex - 1); |
| 660 } |
| 661 |
| 662 var length = array.arrayLength(); |
| 663 for (var i = 0; i < length; ++i) { |
| 664 var element = elements[i]; |
| 665 if (!element) |
| 666 continue; |
| 667 |
| 668 if (i - lastNonEmptyIndex > 1) { |
| 669 appendUndefined(titleElement, i); |
| 670 titleElement.createTextChild(', '); |
111 } | 671 } |
112 return defaultConsoleRowHeight; | 672 |
113 }, | 673 titleElement.appendChild(element); |
| 674 lastNonEmptyIndex = i; |
| 675 if (i < length - 1) |
| 676 titleElement.createTextChild(', '); |
| 677 } |
| 678 appendUndefined(titleElement, length); |
| 679 |
| 680 titleElement.createTextChild(']'); |
| 681 |
| 682 var section = new WebInspector.ObjectPropertiesSection(array, titleElement
, this._linkifier); |
| 683 section.element.classList.add('console-view-object-properties-section'); |
| 684 section.enableContextMenu(); |
| 685 result.appendChild(section.element); |
| 686 } |
| 687 } |
| 688 |
| 689 /** |
| 690 * @param {!WebInspector.RemoteObject} output |
| 691 * @return {!Element} |
| 692 */ |
| 693 _formatParameterAsString(output) { |
| 694 var span = createElement('span'); |
| 695 span.appendChild(WebInspector.linkifyStringAsFragment(output.description ||
'')); |
| 696 |
| 697 var result = createElement('span'); |
| 698 result.createChild('span', 'object-value-string-quote').textContent = '"'; |
| 699 result.appendChild(span); |
| 700 result.createChild('span', 'object-value-string-quote').textContent = '"'; |
| 701 return result; |
| 702 } |
| 703 |
| 704 /** |
| 705 * @param {!WebInspector.RemoteObject} output |
| 706 * @return {!Element} |
| 707 */ |
| 708 _formatParameterAsError(output) { |
| 709 var result = createElement('span'); |
| 710 var errorSpan = this._tryFormatAsError(output.description || ''); |
| 711 result.appendChild(errorSpan ? errorSpan : WebInspector.linkifyStringAsFragm
ent(output.description || '')); |
| 712 return result; |
| 713 } |
| 714 |
| 715 /** |
| 716 * @param {!WebInspector.RemoteObject} output |
| 717 * @return {!Element} |
| 718 */ |
| 719 _formatAsArrayEntry(output) { |
| 720 return this._previewFormatter.renderPropertyPreview(output.type, output.subt
ype, output.description); |
| 721 } |
| 722 |
| 723 /** |
| 724 * @param {?WebInspector.RemoteObject} object |
| 725 * @param {!Array.<string>} propertyPath |
| 726 * @param {boolean} isArrayEntry |
| 727 * @return {!Element} |
| 728 */ |
| 729 _formatAsAccessorProperty(object, propertyPath, isArrayEntry) { |
| 730 var rootElement = WebInspector.ObjectPropertyTreeElement.createRemoteObjectA
ccessorPropertySpan( |
| 731 object, propertyPath, onInvokeGetterClick.bind(this)); |
114 | 732 |
115 /** | 733 /** |
116 * @return {!WebInspector.ConsoleMessage} | 734 * @param {?WebInspector.RemoteObject} result |
| 735 * @param {boolean=} wasThrown |
| 736 * @this {WebInspector.ConsoleViewMessage} |
117 */ | 737 */ |
118 consoleMessage: function() | 738 function onInvokeGetterClick(result, wasThrown) { |
119 { | 739 if (!result) |
120 return this._message; | 740 return; |
121 }, | 741 rootElement.removeChildren(); |
| 742 if (wasThrown) { |
| 743 var element = rootElement.createChild('span'); |
| 744 element.textContent = WebInspector.UIString('<exception>'); |
| 745 element.title = /** @type {string} */ (result.description); |
| 746 } else if (isArrayEntry) { |
| 747 rootElement.appendChild(this._formatAsArrayEntry(result)); |
| 748 } else { |
| 749 // Make a PropertyPreview from the RemoteObject similar to the backend l
ogic. |
| 750 const maxLength = 100; |
| 751 var type = result.type; |
| 752 var subtype = result.subtype; |
| 753 var description = ''; |
| 754 if (type !== 'function' && result.description) { |
| 755 if (type === 'string' || subtype === 'regexp') |
| 756 description = result.description.trimMiddle(maxLength); |
| 757 else |
| 758 description = result.description.trimEnd(maxLength); |
| 759 } |
| 760 rootElement.appendChild(this._previewFormatter.renderPropertyPreview(typ
e, subtype, description)); |
| 761 } |
| 762 } |
| 763 |
| 764 return rootElement; |
| 765 } |
| 766 |
| 767 /** |
| 768 * @param {string} format |
| 769 * @param {!Array.<!WebInspector.RemoteObject>} parameters |
| 770 * @param {!Element} formattedResult |
| 771 */ |
| 772 _formatWithSubstitutionString(format, parameters, formattedResult) { |
| 773 var formatters = {}; |
122 | 774 |
123 /** | 775 /** |
124 * @param {!WebInspector.ConsoleMessage} consoleMessage | 776 * @param {boolean} force |
125 * @return {!Element} | |
126 */ | |
127 _buildTableMessage: function(consoleMessage) | |
128 { | |
129 var formattedMessage = createElement("span"); | |
130 WebInspector.appendStyle(formattedMessage, "components/objectValue.css")
; | |
131 formattedMessage.className = "console-message-text source-code"; | |
132 var anchorElement = this._buildMessageAnchor(consoleMessage); | |
133 if (anchorElement) | |
134 formattedMessage.appendChild(anchorElement); | |
135 | |
136 var table = consoleMessage.parameters && consoleMessage.parameters.lengt
h ? consoleMessage.parameters[0] : null; | |
137 if (table) | |
138 table = this._parameterToRemoteObject(table, this._target()); | |
139 if (!table || !table.preview) | |
140 return formattedMessage; | |
141 | |
142 var columnNames = []; | |
143 var preview = table.preview; | |
144 var rows = []; | |
145 for (var i = 0; i < preview.properties.length; ++i) { | |
146 var rowProperty = preview.properties[i]; | |
147 var rowPreview = rowProperty.valuePreview; | |
148 if (!rowPreview) | |
149 continue; | |
150 | |
151 var rowValue = {}; | |
152 const maxColumnsToRender = 20; | |
153 for (var j = 0; j < rowPreview.properties.length; ++j) { | |
154 var cellProperty = rowPreview.properties[j]; | |
155 var columnRendered = columnNames.indexOf(cellProperty.name) !==
-1; | |
156 if (!columnRendered) { | |
157 if (columnNames.length === maxColumnsToRender) | |
158 continue; | |
159 columnRendered = true; | |
160 columnNames.push(cellProperty.name); | |
161 } | |
162 | |
163 if (columnRendered) { | |
164 var cellElement = this._renderPropertyPreviewOrAccessor(tabl
e, [rowProperty, cellProperty]); | |
165 cellElement.classList.add("console-message-nowrap-below"); | |
166 rowValue[cellProperty.name] = cellElement; | |
167 } | |
168 } | |
169 rows.push([rowProperty.name, rowValue]); | |
170 } | |
171 | |
172 var flatValues = []; | |
173 for (var i = 0; i < rows.length; ++i) { | |
174 var rowName = rows[i][0]; | |
175 var rowValue = rows[i][1]; | |
176 flatValues.push(rowName); | |
177 for (var j = 0; j < columnNames.length; ++j) | |
178 flatValues.push(rowValue[columnNames[j]]); | |
179 } | |
180 columnNames.unshift(WebInspector.UIString("(index)")); | |
181 | |
182 if (flatValues.length) { | |
183 this._dataGrid = WebInspector.SortableDataGrid.create(columnNames, f
latValues); | |
184 | |
185 var formattedResult = createElement("span"); | |
186 var tableElement = formattedResult.createChild("div", "console-messa
ge-formatted-table"); | |
187 var dataGridContainer = tableElement.createChild("span"); | |
188 tableElement.appendChild(this._formatParameter(table, true, false)); | |
189 dataGridContainer.appendChild(this._dataGrid.element); | |
190 formattedMessage.appendChild(formattedResult); | |
191 this._dataGrid.renderInline(); | |
192 } | |
193 return formattedMessage; | |
194 }, | |
195 | |
196 /** | |
197 * @param {!WebInspector.ConsoleMessage} consoleMessage | |
198 * @return {!Element} | |
199 */ | |
200 _buildMessage: function(consoleMessage) | |
201 { | |
202 var messageElement; | |
203 if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.
ConsoleAPI) { | |
204 switch (consoleMessage.type) { | |
205 case WebInspector.ConsoleMessage.MessageType.Trace: | |
206 messageElement = this._format(consoleMessage.parameters || ["con
sole.trace"]); | |
207 break; | |
208 case WebInspector.ConsoleMessage.MessageType.Clear: | |
209 messageElement = createElementWithClass("span", "console-info"); | |
210 messageElement.textContent = WebInspector.UIString("Console was
cleared"); | |
211 break; | |
212 case WebInspector.ConsoleMessage.MessageType.Assert: | |
213 var args = [WebInspector.UIString("Assertion failed:")]; | |
214 if (consoleMessage.parameters) | |
215 args = args.concat(consoleMessage.parameters); | |
216 messageElement = this._format(args); | |
217 break; | |
218 case WebInspector.ConsoleMessage.MessageType.Dir: | |
219 var obj = consoleMessage.parameters ? consoleMessage.parameters[
0] : undefined; | |
220 var args = ["%O", obj]; | |
221 messageElement = this._format(args); | |
222 break; | |
223 case WebInspector.ConsoleMessage.MessageType.Profile: | |
224 case WebInspector.ConsoleMessage.MessageType.ProfileEnd: | |
225 messageElement = this._format([consoleMessage.messageText]); | |
226 break; | |
227 default: | |
228 if (consoleMessage.parameters && consoleMessage.parameters.lengt
h === 1 && consoleMessage.parameters[0].type === "string") | |
229 messageElement = this._tryFormatAsError(/** @type {string} *
/(consoleMessage.parameters[0].value)); | |
230 var args = consoleMessage.parameters || [consoleMessage.messageT
ext]; | |
231 messageElement = messageElement || this._format(args); | |
232 } | |
233 } else if (consoleMessage.source === WebInspector.ConsoleMessage.Message
Source.Network) { | |
234 if (consoleMessage.request) { | |
235 messageElement = createElement("span"); | |
236 if (consoleMessage.level === WebInspector.ConsoleMessage.Message
Level.Error || consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel
.RevokedError) { | |
237 messageElement.createTextChildren(consoleMessage.request.req
uestMethod, " "); | |
238 messageElement.appendChild(WebInspector.Linkifier.linkifyUsi
ngRevealer(consoleMessage.request, consoleMessage.request.url, consoleMessage.re
quest.url)); | |
239 if (consoleMessage.request.failed) | |
240 messageElement.createTextChildren(" ", consoleMessage.re
quest.localizedFailDescription); | |
241 else | |
242 messageElement.createTextChildren(" ", String(consoleMes
sage.request.statusCode), " (", consoleMessage.request.statusText, ")"); | |
243 } else { | |
244 var fragment = WebInspector.linkifyStringAsFragmentWithCusto
mLinkifier(consoleMessage.messageText, linkifyRequest.bind(consoleMessage)); | |
245 messageElement.appendChild(fragment); | |
246 } | |
247 } else { | |
248 messageElement = this._format([consoleMessage.messageText]); | |
249 } | |
250 } else { | |
251 var args = consoleMessage.parameters || [consoleMessage.messageText]
; | |
252 messageElement = this._format(args); | |
253 } | |
254 | |
255 var formattedMessage = createElement("span"); | |
256 WebInspector.appendStyle(formattedMessage, "components/objectValue.css")
; | |
257 formattedMessage.className = "console-message-text source-code"; | |
258 | |
259 var anchorElement = this._buildMessageAnchor(consoleMessage); | |
260 if (anchorElement) | |
261 formattedMessage.appendChild(anchorElement); | |
262 formattedMessage.appendChild(messageElement); | |
263 return formattedMessage; | |
264 | |
265 /** | |
266 * @param {string} title | |
267 * @return {!Element} | |
268 * @this {WebInspector.ConsoleMessage} | |
269 */ | |
270 function linkifyRequest(title) | |
271 { | |
272 return WebInspector.Linkifier.linkifyUsingRevealer(/** @type {!WebIn
spector.NetworkRequest} */ (this.request), title, this.request.url); | |
273 } | |
274 }, | |
275 | |
276 /** | |
277 * @param {!WebInspector.ConsoleMessage} consoleMessage | |
278 * @return {?Element} | |
279 */ | |
280 _buildMessageAnchor: function(consoleMessage) | |
281 { | |
282 var anchorElement = null; | |
283 if (consoleMessage.source !== WebInspector.ConsoleMessage.MessageSource.
Network || consoleMessage.request) { | |
284 if (consoleMessage.scriptId) | |
285 anchorElement = this._linkifyScriptId(consoleMessage.scriptId, c
onsoleMessage.url || "", consoleMessage.line, consoleMessage.column); | |
286 else if (consoleMessage.stackTrace && consoleMessage.stackTrace.call
Frames.length) | |
287 anchorElement = this._linkifyStackTraceTopFrame(consoleMessage.s
tackTrace); | |
288 else if (consoleMessage.url && consoleMessage.url !== "undefined") | |
289 anchorElement = this._linkifyLocation(consoleMessage.url, consol
eMessage.line, consoleMessage.column); | |
290 } else if (consoleMessage.url) { | |
291 var url = consoleMessage.url; | |
292 var isExternal = !WebInspector.resourceForURL(url) && !WebInspector.
networkMapping.uiSourceCodeForURLForAnyTarget(url); | |
293 anchorElement = WebInspector.linkifyURLAsNode(url, url, "console-mes
sage-url", isExternal); | |
294 } | |
295 | |
296 // Append a space to prevent the anchor text from being glued to the con
sole message when the user selects and copies the console messages. | |
297 if (anchorElement) | |
298 anchorElement.appendChild(createTextNode(" ")); | |
299 return anchorElement; | |
300 }, | |
301 | |
302 /** | |
303 * @param {!WebInspector.ConsoleMessage} consoleMessage | |
304 * @param {!WebInspector.Target} target | |
305 * @param {!WebInspector.Linkifier} linkifier | |
306 * @return {!Element} | |
307 */ | |
308 _buildMessageWithStackTrace: function(consoleMessage, target, linkifier) | |
309 { | |
310 var toggleElement = createElementWithClass("div", "console-message-stack
-trace-toggle"); | |
311 var triangleElement = toggleElement.createChild("div", "console-message-
stack-trace-triangle"); | |
312 var contentElement = toggleElement.createChild("div", "console-message-s
tack-trace-wrapper"); | |
313 | |
314 var messageElement = this._buildMessage(consoleMessage); | |
315 var clickableElement = contentElement.createChild("div"); | |
316 clickableElement.appendChild(messageElement); | |
317 var stackTraceElement = contentElement.createChild("div"); | |
318 var stackTracePreview = WebInspector.DOMPresentationUtils.buildStackTrac
ePreviewContents(target, linkifier, consoleMessage.stackTrace); | |
319 stackTraceElement.appendChild(stackTracePreview); | |
320 stackTraceElement.classList.add("hidden"); | |
321 | |
322 /** | |
323 * @param {boolean} expand | |
324 */ | |
325 function expandStackTrace(expand) | |
326 { | |
327 stackTraceElement.classList.toggle("hidden", !expand); | |
328 toggleElement.classList.toggle("expanded", expand); | |
329 } | |
330 | |
331 /** | |
332 * @param {?Event} event | |
333 */ | |
334 function toggleStackTrace(event) | |
335 { | |
336 if (event.target.hasSelection()) | |
337 return; | |
338 expandStackTrace(stackTraceElement.classList.contains("hidden")); | |
339 event.consume(); | |
340 } | |
341 | |
342 clickableElement.addEventListener("click", toggleStackTrace, false); | |
343 triangleElement.addEventListener("click", toggleStackTrace, false); | |
344 if (consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trac
e) | |
345 expandStackTrace(true); | |
346 | |
347 toggleElement._expandStackTraceForTest = expandStackTrace.bind(null, tru
e); | |
348 return toggleElement; | |
349 }, | |
350 | |
351 /** | |
352 * @param {string} url | |
353 * @param {number} lineNumber | |
354 * @param {number} columnNumber | |
355 * @return {?Element} | |
356 */ | |
357 _linkifyLocation: function(url, lineNumber, columnNumber) | |
358 { | |
359 var target = this._target(); | |
360 if (!target) | |
361 return null; | |
362 return this._linkifier.linkifyScriptLocation(target, null, url, lineNumb
er, columnNumber, "console-message-url"); | |
363 }, | |
364 | |
365 /** | |
366 * @param {!RuntimeAgent.StackTrace} stackTrace | |
367 * @return {?Element} | |
368 */ | |
369 _linkifyStackTraceTopFrame: function(stackTrace) | |
370 { | |
371 var target = this._target(); | |
372 if (!target) | |
373 return null; | |
374 return this._linkifier.linkifyStackTraceTopFrame(target, stackTrace, "co
nsole-message-url"); | |
375 }, | |
376 | |
377 /** | |
378 * @param {string} scriptId | |
379 * @param {string} url | |
380 * @param {number} lineNumber | |
381 * @param {number} columnNumber | |
382 * @return {?Element} | |
383 */ | |
384 _linkifyScriptId: function(scriptId, url, lineNumber, columnNumber) | |
385 { | |
386 var target = this._target(); | |
387 if (!target) | |
388 return null; | |
389 return this._linkifier.linkifyScriptLocation(target, scriptId, url, line
Number, columnNumber, "console-message-url"); | |
390 }, | |
391 | |
392 /** | |
393 * @param {!WebInspector.RemoteObject|!Object|string} parameter | |
394 * @param {?WebInspector.Target} target | |
395 * @return {!WebInspector.RemoteObject} | |
396 */ | |
397 _parameterToRemoteObject: function(parameter, target) | |
398 { | |
399 if (parameter instanceof WebInspector.RemoteObject) | |
400 return parameter; | |
401 if (!target) | |
402 return WebInspector.RemoteObject.fromLocalObject(parameter); | |
403 if (typeof parameter === "object") | |
404 return target.runtimeModel.createRemoteObject(parameter); | |
405 return target.runtimeModel.createRemoteObjectFromPrimitiveValue(paramete
r); | |
406 }, | |
407 | |
408 /** | |
409 * @param {!Array.<!WebInspector.RemoteObject|string>} parameters | |
410 * @return {!Element} | |
411 */ | |
412 _format: function(parameters) | |
413 { | |
414 // This node is used like a Builder. Values are continually appended ont
o it. | |
415 var formattedResult = createElement("span"); | |
416 if (!parameters.length) | |
417 return formattedResult; | |
418 | |
419 // Formatting code below assumes that parameters are all wrappers wherea
s frontend console | |
420 // API allows passing arbitrary values as messages (strings, numbers, et
c.). Wrap them here. | |
421 // FIXME: Only pass runtime wrappers here. | |
422 for (var i = 0; i < parameters.length; ++i) | |
423 parameters[i] = this._parameterToRemoteObject(parameters[i], this._t
arget()); | |
424 | |
425 // There can be string log and string eval result. We distinguish betwee
n them based on message type. | |
426 var shouldFormatMessage = WebInspector.RemoteObject.type((/** @type {!Ar
ray.<!WebInspector.RemoteObject>} **/ (parameters))[0]) === "string" && (this._m
essage.type !== WebInspector.ConsoleMessage.MessageType.Result || this._message.
level === WebInspector.ConsoleMessage.MessageLevel.Error || this._message.level
=== WebInspector.ConsoleMessage.MessageLevel.RevokedError); | |
427 | |
428 // Multiple parameters with the first being a format string. Save unused
substitutions. | |
429 if (shouldFormatMessage) { | |
430 var result = this._formatWithSubstitutionString(/** @type {string} *
*/ (parameters[0].description), parameters.slice(1), formattedResult); | |
431 parameters = result.unusedSubstitutions; | |
432 if (parameters.length) | |
433 formattedResult.createTextChild(" "); | |
434 } | |
435 | |
436 // Single parameter, or unused substitutions from above. | |
437 for (var i = 0; i < parameters.length; ++i) { | |
438 // Inline strings when formatting. | |
439 if (shouldFormatMessage && parameters[i].type === "string") | |
440 formattedResult.appendChild(WebInspector.linkifyStringAsFragment
(parameters[i].description)); | |
441 else | |
442 formattedResult.appendChild(this._formatParameter(parameters[i],
false, true)); | |
443 if (i < parameters.length - 1) | |
444 formattedResult.createTextChild(" "); | |
445 } | |
446 return formattedResult; | |
447 }, | |
448 | |
449 /** | |
450 * @param {!WebInspector.RemoteObject} output | |
451 * @param {boolean=} forceObjectFormat | |
452 * @param {boolean=} includePreview | |
453 * @return {!Element} | |
454 */ | |
455 _formatParameter: function(output, forceObjectFormat, includePreview) | |
456 { | |
457 if (output.customPreview()) | |
458 return (new WebInspector.CustomPreviewComponent(output)).element; | |
459 | |
460 var type = forceObjectFormat ? "object" : (output.subtype || output.type
); | |
461 var element; | |
462 switch (type) { | |
463 case "array": | |
464 case "typedarray": | |
465 element = this._formatParameterAsArray(output); | |
466 break; | |
467 case "error": | |
468 element = this._formatParameterAsError(output); | |
469 break; | |
470 case "function": | |
471 case "generator": | |
472 element = this._formatParameterAsFunction(output, includePreview); | |
473 break; | |
474 case "iterator": | |
475 case "map": | |
476 case "object": | |
477 case "promise": | |
478 case "proxy": | |
479 case "set": | |
480 element = this._formatParameterAsObject(output, includePreview); | |
481 break; | |
482 case "node": | |
483 element = this._formatParameterAsNode(output); | |
484 break; | |
485 case "string": | |
486 element = this._formatParameterAsString(output); | |
487 break; | |
488 case "boolean": | |
489 case "date": | |
490 case "null": | |
491 case "number": | |
492 case "regexp": | |
493 case "symbol": | |
494 case "undefined": | |
495 element = this._formatParameterAsValue(output); | |
496 break; | |
497 default: | |
498 element = this._formatParameterAsValue(output); | |
499 console.error("Tried to format remote object of unknown type."); | |
500 } | |
501 element.classList.add("object-value-" + type); | |
502 element.classList.add("source-code"); | |
503 return element; | |
504 }, | |
505 | |
506 /** | |
507 * @param {!WebInspector.RemoteObject} obj | 777 * @param {!WebInspector.RemoteObject} obj |
508 * @return {!Element} | 778 * @return {!Element} |
| 779 * @this {WebInspector.ConsoleViewMessage} |
509 */ | 780 */ |
510 _formatParameterAsValue: function(obj) | 781 function parameterFormatter(force, obj) { |
511 { | 782 return this._formatParameter(obj, force, false); |
512 var result = createElement("span"); | 783 } |
513 result.createTextChild(obj.description || ""); | 784 |
514 if (obj.objectId) | 785 function stringFormatter(obj) { |
515 result.addEventListener("contextmenu", this._contextMenuEventFired.b
ind(this, obj), false); | 786 return obj.description; |
516 return result; | 787 } |
517 }, | 788 |
| 789 function floatFormatter(obj) { |
| 790 if (typeof obj.value !== 'number') |
| 791 return 'NaN'; |
| 792 return obj.value; |
| 793 } |
| 794 |
| 795 function integerFormatter(obj) { |
| 796 if (typeof obj.value !== 'number') |
| 797 return 'NaN'; |
| 798 return Math.floor(obj.value); |
| 799 } |
| 800 |
| 801 function bypassFormatter(obj) { |
| 802 return (obj instanceof Node) ? obj : ''; |
| 803 } |
| 804 |
| 805 var currentStyle = null; |
| 806 function styleFormatter(obj) { |
| 807 currentStyle = {}; |
| 808 var buffer = createElement('span'); |
| 809 buffer.setAttribute('style', obj.description); |
| 810 for (var i = 0; i < buffer.style.length; i++) { |
| 811 var property = buffer.style[i]; |
| 812 if (isWhitelistedProperty(property)) |
| 813 currentStyle[property] = buffer.style[property]; |
| 814 } |
| 815 } |
| 816 |
| 817 function isWhitelistedProperty(property) { |
| 818 var prefixes = [ |
| 819 'background', 'border', 'color', 'font', 'line', 'margin', 'padding', 't
ext', '-webkit-background', |
| 820 '-webkit-border', '-webkit-font', '-webkit-margin', '-webkit-padding', '
-webkit-text' |
| 821 ]; |
| 822 for (var i = 0; i < prefixes.length; i++) { |
| 823 if (property.startsWith(prefixes[i])) |
| 824 return true; |
| 825 } |
| 826 return false; |
| 827 } |
| 828 |
| 829 // Firebug uses %o for formatting objects. |
| 830 formatters.o = parameterFormatter.bind(this, false); |
| 831 formatters.s = stringFormatter; |
| 832 formatters.f = floatFormatter; |
| 833 // Firebug allows both %i and %d for formatting integers. |
| 834 formatters.i = integerFormatter; |
| 835 formatters.d = integerFormatter; |
| 836 |
| 837 // Firebug uses %c for styling the message. |
| 838 formatters.c = styleFormatter; |
| 839 |
| 840 // Support %O to force object formatting, instead of the type-based %o forma
tting. |
| 841 formatters.O = parameterFormatter.bind(this, true); |
| 842 |
| 843 formatters._ = bypassFormatter; |
| 844 |
| 845 function append(a, b) { |
| 846 if (b instanceof Node) |
| 847 a.appendChild(b); |
| 848 else if (typeof b !== 'undefined') { |
| 849 var toAppend = WebInspector.linkifyStringAsFragment(String(b)); |
| 850 if (currentStyle) { |
| 851 var wrapper = createElement('span'); |
| 852 wrapper.appendChild(toAppend); |
| 853 applyCurrentStyle(wrapper); |
| 854 for (var i = 0; i < wrapper.children.length; ++i) |
| 855 applyCurrentStyle(wrapper.children[i]); |
| 856 toAppend = wrapper; |
| 857 } |
| 858 a.appendChild(toAppend); |
| 859 } |
| 860 return a; |
| 861 } |
518 | 862 |
519 /** | 863 /** |
520 * @param {!WebInspector.RemoteObject} obj | 864 * @param {!Element} element |
521 * @param {boolean=} includePreview | |
522 * @return {!Element} | |
523 */ | 865 */ |
524 _formatParameterAsObject: function(obj, includePreview) | 866 function applyCurrentStyle(element) { |
525 { | 867 for (var key in currentStyle) |
526 var titleElement = createElement("span"); | 868 element.style[key] = currentStyle[key]; |
527 if (includePreview && obj.preview) { | 869 } |
528 titleElement.classList.add("console-object-preview"); | 870 |
529 this._previewFormatter.appendObjectPreview(titleElement, obj.preview
); | 871 // String.format does treat formattedResult like a Builder, result is an obj
ect. |
530 } else if (obj.type === "function") { | 872 return String.format(format, parameters, formatters, formattedResult, append
); |
531 WebInspector.ObjectPropertiesSection.formatObjectAsFunction(obj, tit
leElement, false); | 873 } |
532 titleElement.classList.add("object-value-function"); | 874 |
533 } else { | 875 /** |
534 titleElement.createTextChild(obj.description || ""); | 876 * @return {boolean} |
535 } | 877 */ |
536 | 878 matchesFilterRegex(regexObject) { |
537 var section = new WebInspector.ObjectPropertiesSection(obj, titleElement
, this._linkifier); | 879 regexObject.lastIndex = 0; |
538 section.element.classList.add("console-view-object-properties-section"); | 880 var text = this.contentElement().deepTextContent(); |
539 section.enableContextMenu(); | 881 return regexObject.test(text); |
540 return section.element; | 882 } |
541 }, | 883 |
542 | 884 /** |
| 885 * @param {boolean} show |
| 886 */ |
| 887 updateTimestamp(show) { |
| 888 if (!this._contentElement) |
| 889 return; |
| 890 |
| 891 if (show && !this.timestampElement) { |
| 892 this.timestampElement = createElementWithClass('span', 'console-timestamp'
); |
| 893 this.timestampElement.textContent = (new Date(this._message.timestamp)).to
ConsoleTime() + ' '; |
| 894 this._contentElement.insertBefore(this.timestampElement, this._contentElem
ent.firstChild); |
| 895 return; |
| 896 } |
| 897 |
| 898 if (!show && this.timestampElement) { |
| 899 this.timestampElement.remove(); |
| 900 delete this.timestampElement; |
| 901 } |
| 902 } |
| 903 |
| 904 /** |
| 905 * @return {number} |
| 906 */ |
| 907 nestingLevel() { |
| 908 return this._nestingLevel; |
| 909 } |
| 910 |
| 911 resetCloseGroupDecorationCount() { |
| 912 if (!this._closeGroupDecorationCount) |
| 913 return; |
| 914 this._closeGroupDecorationCount = 0; |
| 915 this._updateCloseGroupDecorations(); |
| 916 } |
| 917 |
| 918 incrementCloseGroupDecorationCount() { |
| 919 ++this._closeGroupDecorationCount; |
| 920 this._updateCloseGroupDecorations(); |
| 921 } |
| 922 |
| 923 _updateCloseGroupDecorations() { |
| 924 if (!this._nestingLevelMarkers) |
| 925 return; |
| 926 for (var i = 0, n = this._nestingLevelMarkers.length; i < n; ++i) { |
| 927 var marker = this._nestingLevelMarkers[i]; |
| 928 marker.classList.toggle('group-closed', n - i <= this._closeGroupDecoratio
nCount); |
| 929 } |
| 930 } |
| 931 |
| 932 /** |
| 933 * @return {!Element} |
| 934 */ |
| 935 contentElement() { |
| 936 if (this._contentElement) |
| 937 return this._contentElement; |
| 938 |
| 939 var contentElement = createElementWithClass('div', 'console-message'); |
| 940 this._contentElement = contentElement; |
| 941 if (this._message.type === WebInspector.ConsoleMessage.MessageType.StartGrou
p || |
| 942 this._message.type === WebInspector.ConsoleMessage.MessageType.StartGrou
pCollapsed) |
| 943 contentElement.classList.add('console-group-title'); |
| 944 |
| 945 var formattedMessage; |
| 946 var consoleMessage = this._message; |
| 947 var target = consoleMessage.target(); |
| 948 var shouldIncludeTrace = !!consoleMessage.stackTrace && |
| 949 (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.Net
work || |
| 950 consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error
|| |
| 951 consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Revok
edError || |
| 952 consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trace |
| |
| 953 consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Warni
ng); |
| 954 if (target && shouldIncludeTrace) |
| 955 formattedMessage = this._buildMessageWithStackTrace(consoleMessage, target
, this._linkifier); |
| 956 else if (this._message.type === WebInspector.ConsoleMessage.MessageType.Tabl
e) |
| 957 formattedMessage = this._buildTableMessage(this._message); |
| 958 else |
| 959 formattedMessage = this._buildMessage(consoleMessage); |
| 960 contentElement.appendChild(formattedMessage); |
| 961 |
| 962 this.updateTimestamp(WebInspector.moduleSetting('consoleTimestampsEnabled').
get()); |
| 963 return this._contentElement; |
| 964 } |
| 965 |
| 966 /** |
| 967 * @return {!Element} |
| 968 */ |
| 969 toMessageElement() { |
| 970 if (this._element) |
| 971 return this._element; |
| 972 |
| 973 this._element = createElement('div'); |
| 974 this.updateMessageElement(); |
| 975 return this._element; |
| 976 } |
| 977 |
| 978 updateMessageElement() { |
| 979 if (!this._element) |
| 980 return; |
| 981 |
| 982 this._element.className = 'console-message-wrapper'; |
| 983 this._element.removeChildren(); |
| 984 |
| 985 this._nestingLevelMarkers = []; |
| 986 for (var i = 0; i < this._nestingLevel; ++i) |
| 987 this._nestingLevelMarkers.push(this._element.createChild('div', 'nesting-l
evel-marker')); |
| 988 this._updateCloseGroupDecorations(); |
| 989 this._element.message = this; |
| 990 |
| 991 switch (this._message.level) { |
| 992 case WebInspector.ConsoleMessage.MessageLevel.Log: |
| 993 this._element.classList.add('console-log-level'); |
| 994 break; |
| 995 case WebInspector.ConsoleMessage.MessageLevel.Debug: |
| 996 this._element.classList.add('console-debug-level'); |
| 997 break; |
| 998 case WebInspector.ConsoleMessage.MessageLevel.Warning: |
| 999 this._element.classList.add('console-warning-level'); |
| 1000 break; |
| 1001 case WebInspector.ConsoleMessage.MessageLevel.Error: |
| 1002 this._element.classList.add('console-error-level'); |
| 1003 break; |
| 1004 case WebInspector.ConsoleMessage.MessageLevel.RevokedError: |
| 1005 this._element.classList.add('console-revokedError-level'); |
| 1006 break; |
| 1007 case WebInspector.ConsoleMessage.MessageLevel.Info: |
| 1008 this._element.classList.add('console-info-level'); |
| 1009 break; |
| 1010 } |
| 1011 |
| 1012 this._element.appendChild(this.contentElement()); |
| 1013 if (this._repeatCount > 1) |
| 1014 this._showRepeatCountElement(); |
| 1015 } |
| 1016 |
| 1017 /** |
| 1018 * @return {number} |
| 1019 */ |
| 1020 repeatCount() { |
| 1021 return this._repeatCount || 1; |
| 1022 } |
| 1023 |
| 1024 resetIncrementRepeatCount() { |
| 1025 this._repeatCount = 1; |
| 1026 if (!this._repeatCountElement) |
| 1027 return; |
| 1028 |
| 1029 this._repeatCountElement.remove(); |
| 1030 delete this._repeatCountElement; |
| 1031 } |
| 1032 |
| 1033 incrementRepeatCount() { |
| 1034 this._repeatCount++; |
| 1035 this._showRepeatCountElement(); |
| 1036 } |
| 1037 |
| 1038 _showRepeatCountElement() { |
| 1039 if (!this._contentElement) |
| 1040 return; |
| 1041 |
| 1042 if (!this._repeatCountElement) { |
| 1043 this._repeatCountElement = createElementWithClass('label', 'console-messag
e-repeat-count', 'dt-small-bubble'); |
| 1044 switch (this._message.level) { |
| 1045 case WebInspector.ConsoleMessage.MessageLevel.Warning: |
| 1046 this._repeatCountElement.type = 'warning'; |
| 1047 break; |
| 1048 case WebInspector.ConsoleMessage.MessageLevel.Error: |
| 1049 this._repeatCountElement.type = 'error'; |
| 1050 break; |
| 1051 case WebInspector.ConsoleMessage.MessageLevel.Debug: |
| 1052 this._repeatCountElement.type = 'debug'; |
| 1053 break; |
| 1054 default: |
| 1055 this._repeatCountElement.type = 'info'; |
| 1056 } |
| 1057 this._element.insertBefore(this._repeatCountElement, this._contentElement)
; |
| 1058 this._contentElement.classList.add('repeated-message'); |
| 1059 } |
| 1060 this._repeatCountElement.textContent = this._repeatCount; |
| 1061 } |
| 1062 |
| 1063 get text() { |
| 1064 return this._message.messageText; |
| 1065 } |
| 1066 |
| 1067 /** |
| 1068 * @param {?RegExp} regex |
| 1069 */ |
| 1070 setSearchRegex(regex) { |
| 1071 if (this._searchHiglightNodeChanges && this._searchHiglightNodeChanges.lengt
h) |
| 1072 WebInspector.revertDomChanges(this._searchHiglightNodeChanges); |
| 1073 this._searchRegex = regex; |
| 1074 this._searchHighlightNodes = []; |
| 1075 this._searchHiglightNodeChanges = []; |
| 1076 if (!this._searchRegex) |
| 1077 return; |
| 1078 |
| 1079 var text = this.contentElement().deepTextContent(); |
| 1080 var match; |
| 1081 this._searchRegex.lastIndex = 0; |
| 1082 var sourceRanges = []; |
| 1083 while ((match = this._searchRegex.exec(text)) && match[0]) |
| 1084 sourceRanges.push(new WebInspector.SourceRange(match.index, match[0].lengt
h)); |
| 1085 |
| 1086 if (sourceRanges.length) |
| 1087 this._searchHighlightNodes = |
| 1088 WebInspector.highlightSearchResults(this.contentElement(), sourceRange
s, this._searchHiglightNodeChanges); |
| 1089 } |
| 1090 |
| 1091 /** |
| 1092 * @return {?RegExp} |
| 1093 */ |
| 1094 searchRegex() { |
| 1095 return this._searchRegex; |
| 1096 } |
| 1097 |
| 1098 /** |
| 1099 * @return {number} |
| 1100 */ |
| 1101 searchCount() { |
| 1102 return this._searchHighlightNodes.length; |
| 1103 } |
| 1104 |
| 1105 /** |
| 1106 * @return {!Element} |
| 1107 */ |
| 1108 searchHighlightNode(index) { |
| 1109 return this._searchHighlightNodes[index]; |
| 1110 } |
| 1111 |
| 1112 /** |
| 1113 * @param {string} string |
| 1114 * @return {?Element} |
| 1115 */ |
| 1116 _tryFormatAsError(string) { |
543 /** | 1117 /** |
544 * @param {!WebInspector.RemoteObject} func | 1118 * @param {string} prefix |
545 * @param {boolean=} includePreview | |
546 * @return {!Element} | |
547 */ | 1119 */ |
548 _formatParameterAsFunction: function(func, includePreview) | 1120 function startsWith(prefix) { |
549 { | 1121 return string.startsWith(prefix); |
550 var result = createElement("span"); | 1122 } |
551 WebInspector.RemoteFunction.objectAsFunction(func).targetFunction().then
(formatTargetFunction.bind(this)); | 1123 |
552 return result; | 1124 var errorPrefixes = ['EvalError', 'ReferenceError', 'SyntaxError', 'TypeErro
r', 'RangeError', 'Error', 'URIError']; |
553 | 1125 var target = this._target(); |
554 /** | 1126 if (!target || !errorPrefixes.some(startsWith)) |
555 * @param {!WebInspector.RemoteObject} targetFunction | 1127 return null; |
556 * @this {WebInspector.ConsoleViewMessage} | 1128 var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); |
557 */ | 1129 if (!debuggerModel) |
558 function formatTargetFunction(targetFunction) | 1130 return null; |
559 { | 1131 |
560 var functionElement = createElement("span"); | 1132 var lines = string.split('\n'); |
561 WebInspector.ObjectPropertiesSection.formatObjectAsFunction(targetFu
nction, functionElement, true, includePreview); | 1133 var links = []; |
562 result.appendChild(functionElement); | 1134 var position = 0; |
563 if (targetFunction !== func) { | 1135 for (var i = 0; i < lines.length; ++i) { |
564 var note = result.createChild("span", "object-info-state-note"); | 1136 position += i > 0 ? lines[i - 1].length + 1 : 0; |
565 note.title = WebInspector.UIString("Function was resolved from b
ound function."); | 1137 var isCallFrameLine = /^\s*at\s/.test(lines[i]); |
566 } | 1138 if (!isCallFrameLine && links.length) |
567 result.addEventListener("contextmenu", this._contextMenuEventFired.b
ind(this, targetFunction), false); | 1139 return null; |
568 } | 1140 |
569 }, | 1141 if (!isCallFrameLine) |
570 | 1142 continue; |
571 /** | 1143 |
572 * @param {!WebInspector.RemoteObject} obj | 1144 var openBracketIndex = -1; |
573 * @param {!Event} event | 1145 var closeBracketIndex = -1; |
574 */ | 1146 var match = /\([^\)\(]+\)/.exec(lines[i]); |
575 _contextMenuEventFired: function(obj, event) | 1147 if (match) { |
576 { | 1148 openBracketIndex = match.index; |
577 var contextMenu = new WebInspector.ContextMenu(event); | 1149 closeBracketIndex = match.index + match[0].length - 1; |
578 contextMenu.appendApplicableItems(obj); | 1150 } |
579 contextMenu.show(); | 1151 var hasOpenBracket = openBracketIndex !== -1; |
580 }, | 1152 var left = hasOpenBracket ? openBracketIndex + 1 : lines[i].indexOf('at')
+ 3; |
581 | 1153 var right = hasOpenBracket ? closeBracketIndex : lines[i].length; |
582 /** | 1154 var linkCandidate = lines[i].substring(left, right); |
583 * @param {?WebInspector.RemoteObject} object | 1155 var splitResult = WebInspector.ParsedURL.splitLineAndColumn(linkCandidate)
; |
584 * @param {!Array.<!RuntimeAgent.PropertyPreview>} propertyPath | 1156 if (!splitResult) |
585 * @return {!Element} | 1157 return null; |
586 */ | 1158 |
587 _renderPropertyPreviewOrAccessor: function(object, propertyPath) | 1159 var parsed = splitResult.url.asParsedURL(); |
588 { | 1160 var url; |
589 var property = propertyPath.peekLast(); | 1161 if (parsed) |
590 if (property.type === "accessor") | 1162 url = parsed.url; |
591 return this._formatAsAccessorProperty(object, propertyPath.map(prope
rty => property.name), false); | 1163 else if (debuggerModel.scriptsForSourceURL(splitResult.url).length) |
592 return this._previewFormatter.renderPropertyPreview(property.type, /** @
type {string} */ (property.subtype), property.value); | 1164 url = splitResult.url; |
593 }, | 1165 else if (splitResult.url === '<anonymous>') |
594 | 1166 continue; |
595 /** | 1167 else |
596 * @param {!WebInspector.RemoteObject} object | 1168 return null; |
597 * @return {!Element} | 1169 |
598 */ | 1170 links.push({ |
599 _formatParameterAsNode: function(object) | 1171 url: url, |
600 { | 1172 positionLeft: position + left, |
601 var result = createElement("span"); | 1173 positionRight: position + right, |
602 WebInspector.Renderer.renderPromise(object).then(appendRenderer.bind(thi
s), failedToRender.bind(this)); | 1174 lineNumber: splitResult.lineNumber, |
603 return result; | 1175 columnNumber: splitResult.columnNumber |
604 | 1176 }); |
605 /** | 1177 } |
606 * @param {!Element} rendererElement | 1178 |
607 * @this {WebInspector.ConsoleViewMessage} | 1179 if (!links.length) |
608 */ | 1180 return null; |
609 function appendRenderer(rendererElement) | 1181 |
610 { | 1182 var formattedResult = createElement('span'); |
611 result.appendChild(rendererElement); | 1183 var start = 0; |
612 this._formattedParameterAsNodeForTest(); | 1184 for (var i = 0; i < links.length; ++i) { |
613 } | 1185 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(string.su
bstring(start, links[i].positionLeft))); |
614 | 1186 formattedResult.appendChild(this._linkifier.linkifyScriptLocation( |
615 /** | 1187 target, null, links[i].url, links[i].lineNumber, links[i].columnNumber
)); |
616 * @this {WebInspector.ConsoleViewMessage} | 1188 start = links[i].positionRight; |
617 */ | 1189 } |
618 function failedToRender() | 1190 |
619 { | 1191 if (start !== string.length) |
620 result.appendChild(this._formatParameterAsObject(object, false)); | 1192 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(string.su
bstring(start))); |
621 } | 1193 |
622 }, | 1194 return formattedResult; |
623 | 1195 } |
624 _formattedParameterAsNodeForTest: function() | |
625 { | |
626 }, | |
627 | |
628 /** | |
629 * @param {!WebInspector.RemoteObject} array | |
630 * @return {!Element} | |
631 */ | |
632 _formatParameterAsArray: function(array) | |
633 { | |
634 var usePrintedArrayFormat = this._message.type !== WebInspector.ConsoleM
essage.MessageType.DirXML && this._message.type !== WebInspector.ConsoleMessage.
MessageType.Result; | |
635 var isLongArray = array.arrayLength() > 100; | |
636 if (usePrintedArrayFormat || isLongArray) | |
637 return this._formatParameterAsObject(array, usePrintedArrayFormat ||
!isLongArray); | |
638 var result = createElement("span"); | |
639 array.getAllProperties(false, printArrayResult.bind(this)); | |
640 return result; | |
641 | |
642 /** | |
643 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties | |
644 * @this {!WebInspector.ConsoleViewMessage} | |
645 */ | |
646 function printArrayResult(properties) | |
647 { | |
648 if (!properties) { | |
649 result.appendChild(this._formatParameterAsObject(array, false)); | |
650 return; | |
651 } | |
652 | |
653 var titleElement = createElement("span"); | |
654 var elements = {}; | |
655 for (var i = 0; i < properties.length; ++i) { | |
656 var property = properties[i]; | |
657 var name = property.name; | |
658 if (isNaN(name)) | |
659 continue; | |
660 if (property.getter) | |
661 elements[name] = this._formatAsAccessorProperty(array, [name
], true); | |
662 else if (property.value) | |
663 elements[name] = this._formatAsArrayEntry(property.value); | |
664 } | |
665 | |
666 titleElement.createTextChild("["); | |
667 var lastNonEmptyIndex = -1; | |
668 | |
669 function appendUndefined(titleElement, index) | |
670 { | |
671 if (index - lastNonEmptyIndex <= 1) | |
672 return; | |
673 var span = titleElement.createChild("span", "object-value-undefi
ned"); | |
674 span.textContent = WebInspector.UIString("undefined × %d", index
- lastNonEmptyIndex - 1); | |
675 } | |
676 | |
677 var length = array.arrayLength(); | |
678 for (var i = 0; i < length; ++i) { | |
679 var element = elements[i]; | |
680 if (!element) | |
681 continue; | |
682 | |
683 if (i - lastNonEmptyIndex > 1) { | |
684 appendUndefined(titleElement, i); | |
685 titleElement.createTextChild(", "); | |
686 } | |
687 | |
688 titleElement.appendChild(element); | |
689 lastNonEmptyIndex = i; | |
690 if (i < length - 1) | |
691 titleElement.createTextChild(", "); | |
692 } | |
693 appendUndefined(titleElement, length); | |
694 | |
695 titleElement.createTextChild("]"); | |
696 | |
697 var section = new WebInspector.ObjectPropertiesSection(array, titleE
lement, this._linkifier); | |
698 section.element.classList.add("console-view-object-properties-sectio
n"); | |
699 section.enableContextMenu(); | |
700 result.appendChild(section.element); | |
701 } | |
702 }, | |
703 | |
704 /** | |
705 * @param {!WebInspector.RemoteObject} output | |
706 * @return {!Element} | |
707 */ | |
708 _formatParameterAsString: function(output) | |
709 { | |
710 var span = createElement("span"); | |
711 span.appendChild(WebInspector.linkifyStringAsFragment(output.description
|| "")); | |
712 | |
713 var result = createElement("span"); | |
714 result.createChild("span", "object-value-string-quote").textContent = "\
""; | |
715 result.appendChild(span); | |
716 result.createChild("span", "object-value-string-quote").textContent = "\
""; | |
717 return result; | |
718 }, | |
719 | |
720 /** | |
721 * @param {!WebInspector.RemoteObject} output | |
722 * @return {!Element} | |
723 */ | |
724 _formatParameterAsError: function(output) | |
725 { | |
726 var result = createElement("span"); | |
727 var errorSpan = this._tryFormatAsError(output.description || ""); | |
728 result.appendChild(errorSpan ? errorSpan : WebInspector.linkifyStringAsF
ragment(output.description || "")); | |
729 return result; | |
730 }, | |
731 | |
732 /** | |
733 * @param {!WebInspector.RemoteObject} output | |
734 * @return {!Element} | |
735 */ | |
736 _formatAsArrayEntry: function(output) | |
737 { | |
738 return this._previewFormatter.renderPropertyPreview(output.type, output.
subtype, output.description); | |
739 }, | |
740 | |
741 /** | |
742 * @param {?WebInspector.RemoteObject} object | |
743 * @param {!Array.<string>} propertyPath | |
744 * @param {boolean} isArrayEntry | |
745 * @return {!Element} | |
746 */ | |
747 _formatAsAccessorProperty: function(object, propertyPath, isArrayEntry) | |
748 { | |
749 var rootElement = WebInspector.ObjectPropertyTreeElement.createRemoteObj
ectAccessorPropertySpan(object, propertyPath, onInvokeGetterClick.bind(this)); | |
750 | |
751 /** | |
752 * @param {?WebInspector.RemoteObject} result | |
753 * @param {boolean=} wasThrown | |
754 * @this {WebInspector.ConsoleViewMessage} | |
755 */ | |
756 function onInvokeGetterClick(result, wasThrown) | |
757 { | |
758 if (!result) | |
759 return; | |
760 rootElement.removeChildren(); | |
761 if (wasThrown) { | |
762 var element = rootElement.createChild("span"); | |
763 element.textContent = WebInspector.UIString("<exception>"); | |
764 element.title = /** @type {string} */ (result.description); | |
765 } else if (isArrayEntry) { | |
766 rootElement.appendChild(this._formatAsArrayEntry(result)); | |
767 } else { | |
768 // Make a PropertyPreview from the RemoteObject similar to the b
ackend logic. | |
769 const maxLength = 100; | |
770 var type = result.type; | |
771 var subtype = result.subtype; | |
772 var description = ""; | |
773 if (type !== "function" && result.description) { | |
774 if (type === "string" || subtype === "regexp") | |
775 description = result.description.trimMiddle(maxLength); | |
776 else | |
777 description = result.description.trimEnd(maxLength); | |
778 } | |
779 rootElement.appendChild(this._previewFormatter.renderPropertyPre
view(type, subtype, description)); | |
780 } | |
781 } | |
782 | |
783 return rootElement; | |
784 }, | |
785 | |
786 /** | |
787 * @param {string} format | |
788 * @param {!Array.<!WebInspector.RemoteObject>} parameters | |
789 * @param {!Element} formattedResult | |
790 */ | |
791 _formatWithSubstitutionString: function(format, parameters, formattedResult) | |
792 { | |
793 var formatters = {}; | |
794 | |
795 /** | |
796 * @param {boolean} force | |
797 * @param {!WebInspector.RemoteObject} obj | |
798 * @return {!Element} | |
799 * @this {WebInspector.ConsoleViewMessage} | |
800 */ | |
801 function parameterFormatter(force, obj) | |
802 { | |
803 return this._formatParameter(obj, force, false); | |
804 } | |
805 | |
806 function stringFormatter(obj) | |
807 { | |
808 return obj.description; | |
809 } | |
810 | |
811 function floatFormatter(obj) | |
812 { | |
813 if (typeof obj.value !== "number") | |
814 return "NaN"; | |
815 return obj.value; | |
816 } | |
817 | |
818 function integerFormatter(obj) | |
819 { | |
820 if (typeof obj.value !== "number") | |
821 return "NaN"; | |
822 return Math.floor(obj.value); | |
823 } | |
824 | |
825 function bypassFormatter(obj) | |
826 { | |
827 return (obj instanceof Node) ? obj : ""; | |
828 } | |
829 | |
830 var currentStyle = null; | |
831 function styleFormatter(obj) | |
832 { | |
833 currentStyle = {}; | |
834 var buffer = createElement("span"); | |
835 buffer.setAttribute("style", obj.description); | |
836 for (var i = 0; i < buffer.style.length; i++) { | |
837 var property = buffer.style[i]; | |
838 if (isWhitelistedProperty(property)) | |
839 currentStyle[property] = buffer.style[property]; | |
840 } | |
841 } | |
842 | |
843 function isWhitelistedProperty(property) | |
844 { | |
845 var prefixes = ["background", "border", "color", "font", "line", "ma
rgin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font"
, "-webkit-margin", "-webkit-padding", "-webkit-text"]; | |
846 for (var i = 0; i < prefixes.length; i++) { | |
847 if (property.startsWith(prefixes[i])) | |
848 return true; | |
849 } | |
850 return false; | |
851 } | |
852 | |
853 // Firebug uses %o for formatting objects. | |
854 formatters.o = parameterFormatter.bind(this, false); | |
855 formatters.s = stringFormatter; | |
856 formatters.f = floatFormatter; | |
857 // Firebug allows both %i and %d for formatting integers. | |
858 formatters.i = integerFormatter; | |
859 formatters.d = integerFormatter; | |
860 | |
861 // Firebug uses %c for styling the message. | |
862 formatters.c = styleFormatter; | |
863 | |
864 // Support %O to force object formatting, instead of the type-based %o f
ormatting. | |
865 formatters.O = parameterFormatter.bind(this, true); | |
866 | |
867 formatters._ = bypassFormatter; | |
868 | |
869 function append(a, b) | |
870 { | |
871 if (b instanceof Node) | |
872 a.appendChild(b); | |
873 else if (typeof b !== "undefined") { | |
874 var toAppend = WebInspector.linkifyStringAsFragment(String(b)); | |
875 if (currentStyle) { | |
876 var wrapper = createElement("span"); | |
877 wrapper.appendChild(toAppend); | |
878 applyCurrentStyle(wrapper); | |
879 for (var i = 0; i < wrapper.children.length; ++i) | |
880 applyCurrentStyle(wrapper.children[i]); | |
881 toAppend = wrapper; | |
882 } | |
883 a.appendChild(toAppend); | |
884 } | |
885 return a; | |
886 } | |
887 | |
888 /** | |
889 * @param {!Element} element | |
890 */ | |
891 function applyCurrentStyle(element) | |
892 { | |
893 for (var key in currentStyle) | |
894 element.style[key] = currentStyle[key]; | |
895 } | |
896 | |
897 // String.format does treat formattedResult like a Builder, result is an
object. | |
898 return String.format(format, parameters, formatters, formattedResult, ap
pend); | |
899 }, | |
900 | |
901 /** | |
902 * @return {boolean} | |
903 */ | |
904 matchesFilterRegex: function(regexObject) | |
905 { | |
906 regexObject.lastIndex = 0; | |
907 var text = this.contentElement().deepTextContent(); | |
908 return regexObject.test(text); | |
909 }, | |
910 | |
911 /** | |
912 * @param {boolean} show | |
913 */ | |
914 updateTimestamp: function(show) | |
915 { | |
916 if (!this._contentElement) | |
917 return; | |
918 | |
919 if (show && !this.timestampElement) { | |
920 this.timestampElement = createElementWithClass("span", "console-time
stamp"); | |
921 this.timestampElement.textContent = (new Date(this._message.timestam
p)).toConsoleTime() + " "; | |
922 this._contentElement.insertBefore(this.timestampElement, this._conte
ntElement.firstChild); | |
923 return; | |
924 } | |
925 | |
926 if (!show && this.timestampElement) { | |
927 this.timestampElement.remove(); | |
928 delete this.timestampElement; | |
929 } | |
930 }, | |
931 | |
932 /** | |
933 * @return {number} | |
934 */ | |
935 nestingLevel: function() | |
936 { | |
937 return this._nestingLevel; | |
938 }, | |
939 | |
940 resetCloseGroupDecorationCount: function() | |
941 { | |
942 if (!this._closeGroupDecorationCount) | |
943 return; | |
944 this._closeGroupDecorationCount = 0; | |
945 this._updateCloseGroupDecorations(); | |
946 }, | |
947 | |
948 incrementCloseGroupDecorationCount: function() | |
949 { | |
950 ++this._closeGroupDecorationCount; | |
951 this._updateCloseGroupDecorations(); | |
952 }, | |
953 | |
954 _updateCloseGroupDecorations: function() | |
955 { | |
956 if (!this._nestingLevelMarkers) | |
957 return; | |
958 for (var i = 0, n = this._nestingLevelMarkers.length; i < n; ++i) { | |
959 var marker = this._nestingLevelMarkers[i]; | |
960 marker.classList.toggle("group-closed", n - i <= this._closeGroupDec
orationCount); | |
961 } | |
962 }, | |
963 | |
964 /** | |
965 * @return {!Element} | |
966 */ | |
967 contentElement: function() | |
968 { | |
969 if (this._contentElement) | |
970 return this._contentElement; | |
971 | |
972 var contentElement = createElementWithClass("div", "console-message"); | |
973 this._contentElement = contentElement; | |
974 if (this._message.type === WebInspector.ConsoleMessage.MessageType.Start
Group || this._message.type === WebInspector.ConsoleMessage.MessageType.StartGro
upCollapsed) | |
975 contentElement.classList.add("console-group-title"); | |
976 | |
977 var formattedMessage; | |
978 var consoleMessage = this._message; | |
979 var target = consoleMessage.target(); | |
980 var shouldIncludeTrace = !!consoleMessage.stackTrace && (consoleMessage.
source === WebInspector.ConsoleMessage.MessageSource.Network || consoleMessage.l
evel === WebInspector.ConsoleMessage.MessageLevel.Error || consoleMessage.level
=== WebInspector.ConsoleMessage.MessageLevel.RevokedError || consoleMessage.type
=== WebInspector.ConsoleMessage.MessageType.Trace || consoleMessage.level === W
ebInspector.ConsoleMessage.MessageLevel.Warning); | |
981 if (target && shouldIncludeTrace) | |
982 formattedMessage = this._buildMessageWithStackTrace(consoleMessage,
target, this._linkifier); | |
983 else if (this._message.type === WebInspector.ConsoleMessage.MessageType.
Table) | |
984 formattedMessage = this._buildTableMessage(this._message); | |
985 else | |
986 formattedMessage = this._buildMessage(consoleMessage); | |
987 contentElement.appendChild(formattedMessage); | |
988 | |
989 this.updateTimestamp(WebInspector.moduleSetting("consoleTimestampsEnable
d").get()); | |
990 return this._contentElement; | |
991 }, | |
992 | |
993 /** | |
994 * @return {!Element} | |
995 */ | |
996 toMessageElement: function() | |
997 { | |
998 if (this._element) | |
999 return this._element; | |
1000 | |
1001 this._element = createElement("div"); | |
1002 this.updateMessageElement(); | |
1003 return this._element; | |
1004 }, | |
1005 | |
1006 updateMessageElement: function() | |
1007 { | |
1008 if (!this._element) | |
1009 return; | |
1010 | |
1011 this._element.className = "console-message-wrapper"; | |
1012 this._element.removeChildren(); | |
1013 | |
1014 this._nestingLevelMarkers = []; | |
1015 for (var i = 0; i < this._nestingLevel; ++i) | |
1016 this._nestingLevelMarkers.push(this._element.createChild("div", "nes
ting-level-marker")); | |
1017 this._updateCloseGroupDecorations(); | |
1018 this._element.message = this; | |
1019 | |
1020 switch (this._message.level) { | |
1021 case WebInspector.ConsoleMessage.MessageLevel.Log: | |
1022 this._element.classList.add("console-log-level"); | |
1023 break; | |
1024 case WebInspector.ConsoleMessage.MessageLevel.Debug: | |
1025 this._element.classList.add("console-debug-level"); | |
1026 break; | |
1027 case WebInspector.ConsoleMessage.MessageLevel.Warning: | |
1028 this._element.classList.add("console-warning-level"); | |
1029 break; | |
1030 case WebInspector.ConsoleMessage.MessageLevel.Error: | |
1031 this._element.classList.add("console-error-level"); | |
1032 break; | |
1033 case WebInspector.ConsoleMessage.MessageLevel.RevokedError: | |
1034 this._element.classList.add("console-revokedError-level"); | |
1035 break; | |
1036 case WebInspector.ConsoleMessage.MessageLevel.Info: | |
1037 this._element.classList.add("console-info-level"); | |
1038 break; | |
1039 } | |
1040 | |
1041 this._element.appendChild(this.contentElement()); | |
1042 if (this._repeatCount > 1) | |
1043 this._showRepeatCountElement(); | |
1044 }, | |
1045 | |
1046 /** | |
1047 * @return {number} | |
1048 */ | |
1049 repeatCount: function() | |
1050 { | |
1051 return this._repeatCount || 1; | |
1052 }, | |
1053 | |
1054 resetIncrementRepeatCount: function() | |
1055 { | |
1056 this._repeatCount = 1; | |
1057 if (!this._repeatCountElement) | |
1058 return; | |
1059 | |
1060 this._repeatCountElement.remove(); | |
1061 delete this._repeatCountElement; | |
1062 }, | |
1063 | |
1064 incrementRepeatCount: function() | |
1065 { | |
1066 this._repeatCount++; | |
1067 this._showRepeatCountElement(); | |
1068 }, | |
1069 | |
1070 _showRepeatCountElement: function() | |
1071 { | |
1072 if (!this._contentElement) | |
1073 return; | |
1074 | |
1075 if (!this._repeatCountElement) { | |
1076 this._repeatCountElement = createElementWithClass("label", "console-
message-repeat-count", "dt-small-bubble"); | |
1077 switch (this._message.level) { | |
1078 case WebInspector.ConsoleMessage.MessageLevel.Warning: | |
1079 this._repeatCountElement.type = "warning"; | |
1080 break; | |
1081 case WebInspector.ConsoleMessage.MessageLevel.Error: | |
1082 this._repeatCountElement.type = "error"; | |
1083 break; | |
1084 case WebInspector.ConsoleMessage.MessageLevel.Debug: | |
1085 this._repeatCountElement.type = "debug"; | |
1086 break; | |
1087 default: | |
1088 this._repeatCountElement.type = "info"; | |
1089 } | |
1090 this._element.insertBefore(this._repeatCountElement, this._contentEl
ement); | |
1091 this._contentElement.classList.add("repeated-message"); | |
1092 } | |
1093 this._repeatCountElement.textContent = this._repeatCount; | |
1094 }, | |
1095 | |
1096 get text() | |
1097 { | |
1098 return this._message.messageText; | |
1099 }, | |
1100 | |
1101 /** | |
1102 * @param {?RegExp} regex | |
1103 */ | |
1104 setSearchRegex: function(regex) | |
1105 { | |
1106 if (this._searchHiglightNodeChanges && this._searchHiglightNodeChanges.l
ength) | |
1107 WebInspector.revertDomChanges(this._searchHiglightNodeChanges); | |
1108 this._searchRegex = regex; | |
1109 this._searchHighlightNodes = []; | |
1110 this._searchHiglightNodeChanges = []; | |
1111 if (!this._searchRegex) | |
1112 return; | |
1113 | |
1114 var text = this.contentElement().deepTextContent(); | |
1115 var match; | |
1116 this._searchRegex.lastIndex = 0; | |
1117 var sourceRanges = []; | |
1118 while ((match = this._searchRegex.exec(text)) && match[0]) | |
1119 sourceRanges.push(new WebInspector.SourceRange(match.index, match[0]
.length)); | |
1120 | |
1121 if (sourceRanges.length) | |
1122 this._searchHighlightNodes = WebInspector.highlightSearchResults(thi
s.contentElement(), sourceRanges, this._searchHiglightNodeChanges); | |
1123 }, | |
1124 | |
1125 /** | |
1126 * @return {?RegExp} | |
1127 */ | |
1128 searchRegex: function() | |
1129 { | |
1130 return this._searchRegex; | |
1131 }, | |
1132 | |
1133 /** | |
1134 * @return {number} | |
1135 */ | |
1136 searchCount: function() | |
1137 { | |
1138 return this._searchHighlightNodes.length; | |
1139 }, | |
1140 | |
1141 /** | |
1142 * @return {!Element} | |
1143 */ | |
1144 searchHighlightNode: function(index) | |
1145 { | |
1146 return this._searchHighlightNodes[index]; | |
1147 }, | |
1148 | |
1149 /** | |
1150 * @param {string} string | |
1151 * @return {?Element} | |
1152 */ | |
1153 _tryFormatAsError: function(string) | |
1154 { | |
1155 /** | |
1156 * @param {string} prefix | |
1157 */ | |
1158 function startsWith(prefix) | |
1159 { | |
1160 return string.startsWith(prefix); | |
1161 } | |
1162 | |
1163 var errorPrefixes = ["EvalError", "ReferenceError", "SyntaxError", "Type
Error", "RangeError", "Error", "URIError"]; | |
1164 var target = this._target(); | |
1165 if (!target || !errorPrefixes.some(startsWith)) | |
1166 return null; | |
1167 var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | |
1168 if (!debuggerModel) | |
1169 return null; | |
1170 | |
1171 var lines = string.split("\n"); | |
1172 var links = []; | |
1173 var position = 0; | |
1174 for (var i = 0; i < lines.length; ++i) { | |
1175 position += i > 0 ? lines[i - 1].length + 1 : 0; | |
1176 var isCallFrameLine = /^\s*at\s/.test(lines[i]); | |
1177 if (!isCallFrameLine && links.length) | |
1178 return null; | |
1179 | |
1180 if (!isCallFrameLine) | |
1181 continue; | |
1182 | |
1183 var openBracketIndex = -1; | |
1184 var closeBracketIndex = -1; | |
1185 var match = /\([^\)\(]+\)/.exec(lines[i]); | |
1186 if (match) { | |
1187 openBracketIndex = match.index; | |
1188 closeBracketIndex = match.index + match[0].length - 1; | |
1189 } | |
1190 var hasOpenBracket = openBracketIndex !== -1; | |
1191 var left = hasOpenBracket ? openBracketIndex + 1 : lines[i].indexOf(
"at") + 3; | |
1192 var right = hasOpenBracket ? closeBracketIndex : lines[i].length; | |
1193 var linkCandidate = lines[i].substring(left, right); | |
1194 var splitResult = WebInspector.ParsedURL.splitLineAndColumn(linkCand
idate); | |
1195 if (!splitResult) | |
1196 return null; | |
1197 | |
1198 var parsed = splitResult.url.asParsedURL(); | |
1199 var url; | |
1200 if (parsed) | |
1201 url = parsed.url; | |
1202 else if (debuggerModel.scriptsForSourceURL(splitResult.url).length) | |
1203 url = splitResult.url; | |
1204 else if (splitResult.url === "<anonymous>") | |
1205 continue; | |
1206 else | |
1207 return null; | |
1208 | |
1209 links.push({url: url, positionLeft: position + left, positionRight:
position + right, lineNumber: splitResult.lineNumber, columnNumber: splitResult.
columnNumber}); | |
1210 } | |
1211 | |
1212 if (!links.length) | |
1213 return null; | |
1214 | |
1215 var formattedResult = createElement("span"); | |
1216 var start = 0; | |
1217 for (var i = 0; i < links.length; ++i) { | |
1218 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(str
ing.substring(start, links[i].positionLeft))); | |
1219 formattedResult.appendChild(this._linkifier.linkifyScriptLocation(ta
rget, null, links[i].url, links[i].lineNumber, links[i].columnNumber)); | |
1220 start = links[i].positionRight; | |
1221 } | |
1222 | |
1223 if (start !== string.length) | |
1224 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(str
ing.substring(start))); | |
1225 | |
1226 return formattedResult; | |
1227 } | |
1228 }; | 1196 }; |
1229 | 1197 |
1230 /** | 1198 /** |
1231 * @constructor | 1199 * @unrestricted |
1232 * @extends {WebInspector.ConsoleViewMessage} | |
1233 * @param {!WebInspector.ConsoleMessage} consoleMessage | |
1234 * @param {!WebInspector.Linkifier} linkifier | |
1235 * @param {number} nestingLevel | |
1236 */ | 1200 */ |
1237 WebInspector.ConsoleGroupViewMessage = function(consoleMessage, linkifier, nesti
ngLevel) | 1201 WebInspector.ConsoleGroupViewMessage = class extends WebInspector.ConsoleViewMes
sage { |
1238 { | 1202 /** |
| 1203 * @param {!WebInspector.ConsoleMessage} consoleMessage |
| 1204 * @param {!WebInspector.Linkifier} linkifier |
| 1205 * @param {number} nestingLevel |
| 1206 */ |
| 1207 constructor(consoleMessage, linkifier, nestingLevel) { |
1239 console.assert(consoleMessage.isGroupStartMessage()); | 1208 console.assert(consoleMessage.isGroupStartMessage()); |
1240 WebInspector.ConsoleViewMessage.call(this, consoleMessage, linkifier, nestin
gLevel); | 1209 super(consoleMessage, linkifier, nestingLevel); |
1241 this.setCollapsed(consoleMessage.type === WebInspector.ConsoleMessage.Messag
eType.StartGroupCollapsed); | 1210 this.setCollapsed(consoleMessage.type === WebInspector.ConsoleMessage.Messag
eType.StartGroupCollapsed); |
| 1211 } |
| 1212 |
| 1213 /** |
| 1214 * @param {boolean} collapsed |
| 1215 */ |
| 1216 setCollapsed(collapsed) { |
| 1217 this._collapsed = collapsed; |
| 1218 if (this._element) |
| 1219 this._element.classList.toggle('collapsed', this._collapsed); |
| 1220 } |
| 1221 |
| 1222 /** |
| 1223 * @return {boolean} |
| 1224 */ |
| 1225 collapsed() { |
| 1226 return this._collapsed; |
| 1227 } |
| 1228 |
| 1229 /** |
| 1230 * @override |
| 1231 * @return {!Element} |
| 1232 */ |
| 1233 toMessageElement() { |
| 1234 if (!this._element) { |
| 1235 super.toMessageElement(); |
| 1236 this._element.classList.toggle('collapsed', this._collapsed); |
| 1237 } |
| 1238 return this._element; |
| 1239 } |
1242 }; | 1240 }; |
1243 | |
1244 WebInspector.ConsoleGroupViewMessage.prototype = { | |
1245 /** | |
1246 * @param {boolean} collapsed | |
1247 */ | |
1248 setCollapsed: function(collapsed) | |
1249 { | |
1250 this._collapsed = collapsed; | |
1251 if (this._element) | |
1252 this._element.classList.toggle("collapsed", this._collapsed); | |
1253 }, | |
1254 | |
1255 /** | |
1256 * @return {boolean} | |
1257 */ | |
1258 collapsed: function() | |
1259 { | |
1260 return this._collapsed; | |
1261 }, | |
1262 | |
1263 /** | |
1264 * @override | |
1265 * @return {!Element} | |
1266 */ | |
1267 toMessageElement: function() | |
1268 { | |
1269 if (!this._element) { | |
1270 WebInspector.ConsoleViewMessage.prototype.toMessageElement.call(this
); | |
1271 this._element.classList.toggle("collapsed", this._collapsed); | |
1272 } | |
1273 return this._element; | |
1274 }, | |
1275 | |
1276 __proto__: WebInspector.ConsoleViewMessage.prototype | |
1277 }; | |
OLD | NEW |