OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. | 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
3 * Copyright (C) 2009 Joseph Pecoraro | 3 * Copyright (C) 2009 Joseph Pecoraro |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
11 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
12 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
13 * | 13 * |
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 */ | 25 */ |
26 | |
27 /** | 26 /** |
28 * @constructor | 27 * @unrestricted |
29 * @extends {TreeOutlineInShadow} | |
30 * @param {!WebInspector.RemoteObject} object | |
31 * @param {?string|!Element=} title | |
32 * @param {!WebInspector.Linkifier=} linkifier | |
33 * @param {?string=} emptyPlaceholder | |
34 * @param {boolean=} ignoreHasOwnProperty | |
35 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties | |
36 */ | 28 */ |
37 WebInspector.ObjectPropertiesSection = function(object, title, linkifier, emptyP
laceholder, ignoreHasOwnProperty, extraProperties) | 29 WebInspector.ObjectPropertiesSection = class extends TreeOutlineInShadow { |
38 { | 30 /** |
39 TreeOutlineInShadow.call(this); | 31 * @param {!WebInspector.RemoteObject} object |
| 32 * @param {?string|!Element=} title |
| 33 * @param {!WebInspector.Linkifier=} linkifier |
| 34 * @param {?string=} emptyPlaceholder |
| 35 * @param {boolean=} ignoreHasOwnProperty |
| 36 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties |
| 37 */ |
| 38 constructor(object, title, linkifier, emptyPlaceholder, ignoreHasOwnProperty,
extraProperties) { |
| 39 super(); |
40 this._object = object; | 40 this._object = object; |
41 this._editable = true; | 41 this._editable = true; |
42 this.hideOverflow(); | 42 this.hideOverflow(); |
43 this.setFocusable(false); | 43 this.setFocusable(false); |
44 this._objectTreeElement = new WebInspector.ObjectPropertiesSection.RootEleme
nt(object, linkifier, emptyPlaceholder, ignoreHasOwnProperty, extraProperties); | 44 this._objectTreeElement = new WebInspector.ObjectPropertiesSection.RootEleme
nt( |
| 45 object, linkifier, emptyPlaceholder, ignoreHasOwnProperty, extraProperti
es); |
45 this.appendChild(this._objectTreeElement); | 46 this.appendChild(this._objectTreeElement); |
46 if (typeof title === "string" || !title) { | 47 if (typeof title === 'string' || !title) { |
47 this.titleElement = this.element.createChild("span"); | 48 this.titleElement = this.element.createChild('span'); |
48 this.titleElement.textContent = title || ""; | 49 this.titleElement.textContent = title || ''; |
49 } else { | 50 } else { |
50 this.titleElement = title; | 51 this.titleElement = title; |
51 this.element.appendChild(title); | 52 this.element.appendChild(title); |
52 } | 53 } |
53 | 54 |
54 if (object.description && WebInspector.ObjectPropertiesSection._needsAlterna
teTitle(object)) { | 55 if (object.description && WebInspector.ObjectPropertiesSection._needsAlterna
teTitle(object)) { |
55 this.expandedTitleElement = createElement("span"); | 56 this.expandedTitleElement = createElement('span'); |
56 this.expandedTitleElement.createTextChild(object.description); | 57 this.expandedTitleElement.createTextChild(object.description); |
57 | 58 |
58 var note = this.expandedTitleElement.createChild("span", "object-state-n
ote"); | 59 var note = this.expandedTitleElement.createChild('span', 'object-state-not
e'); |
59 note.classList.add("info-note"); | 60 note.classList.add('info-note'); |
60 note.title = WebInspector.UIString("Value below was evaluated just now."
); | 61 note.title = WebInspector.UIString('Value below was evaluated just now.'); |
61 } | 62 } |
62 | 63 |
63 this.element._section = this; | 64 this.element._section = this; |
64 this.registerRequiredCSS("components/objectValue.css"); | 65 this.registerRequiredCSS('components/objectValue.css'); |
65 this.registerRequiredCSS("components/objectPropertiesSection.css"); | 66 this.registerRequiredCSS('components/objectPropertiesSection.css'); |
66 this.rootElement().childrenListElement.classList.add("source-code", "object-
properties-section"); | 67 this.rootElement().childrenListElement.classList.add('source-code', 'object-
properties-section'); |
67 }; | 68 } |
68 | 69 |
69 /** @const */ | 70 /** |
70 WebInspector.ObjectPropertiesSection._arrayLoadThreshold = 100; | 71 * @param {!WebInspector.RemoteObject} object |
71 | 72 * @param {!WebInspector.Linkifier=} linkifier |
72 /** | 73 * @param {boolean=} skipProto |
73 * @param {!WebInspector.RemoteObject} object | 74 * @return {!Element} |
74 * @param {!WebInspector.Linkifier=} linkifier | 75 */ |
75 * @param {boolean=} skipProto | 76 static defaultObjectPresentation(object, linkifier, skipProto) { |
76 * @return {!Element} | 77 var componentRoot = createElementWithClass('span', 'source-code'); |
77 */ | 78 var shadowRoot = WebInspector.createShadowRootWithCoreStyles(componentRoot,
'components/objectValue.css'); |
78 WebInspector.ObjectPropertiesSection.defaultObjectPresentation = function(object
, linkifier, skipProto) | |
79 { | |
80 var componentRoot = createElementWithClass("span", "source-code"); | |
81 var shadowRoot = WebInspector.createShadowRootWithCoreStyles(componentRoot,
"components/objectValue.css"); | |
82 shadowRoot.appendChild(WebInspector.ObjectPropertiesSection.createValueEleme
nt(object, false)); | 79 shadowRoot.appendChild(WebInspector.ObjectPropertiesSection.createValueEleme
nt(object, false)); |
83 if (!object.hasChildren) | 80 if (!object.hasChildren) |
84 return componentRoot; | 81 return componentRoot; |
85 | 82 |
86 var objectPropertiesSection = new WebInspector.ObjectPropertiesSection(objec
t, componentRoot, linkifier); | 83 var objectPropertiesSection = new WebInspector.ObjectPropertiesSection(objec
t, componentRoot, linkifier); |
87 objectPropertiesSection.editable = false; | 84 objectPropertiesSection.editable = false; |
88 if (skipProto) | 85 if (skipProto) |
89 objectPropertiesSection.skipProto(); | 86 objectPropertiesSection.skipProto(); |
90 | 87 |
91 return objectPropertiesSection.element; | 88 return objectPropertiesSection.element; |
92 }; | 89 } |
93 | 90 |
94 WebInspector.ObjectPropertiesSection.prototype = { | 91 /** |
95 skipProto: function() | 92 * @param {!WebInspector.RemoteObjectProperty} propertyA |
96 { | 93 * @param {!WebInspector.RemoteObjectProperty} propertyB |
97 this._skipProto = true; | 94 * @return {number} |
98 }, | 95 */ |
99 | 96 static CompareProperties(propertyA, propertyB) { |
100 expand: function() | |
101 { | |
102 this._objectTreeElement.expand(); | |
103 }, | |
104 | |
105 /** | |
106 * @param {boolean} value | |
107 */ | |
108 setEditable: function(value) | |
109 { | |
110 this._editable = value; | |
111 }, | |
112 | |
113 /** | |
114 * @return {!TreeElement} | |
115 */ | |
116 objectTreeElement: function() | |
117 { | |
118 return this._objectTreeElement; | |
119 }, | |
120 | |
121 enableContextMenu: function() | |
122 { | |
123 this.element.addEventListener("contextmenu", this._contextMenuEventFired
.bind(this), false); | |
124 }, | |
125 | |
126 _contextMenuEventFired: function(event) | |
127 { | |
128 var contextMenu = new WebInspector.ContextMenu(event); | |
129 contextMenu.appendApplicableItems(this._object); | |
130 contextMenu.show(); | |
131 }, | |
132 | |
133 titleLessMode: function() | |
134 { | |
135 this._objectTreeElement.listItemElement.classList.add("hidden"); | |
136 this._objectTreeElement.childrenListElement.classList.add("title-less-mo
de"); | |
137 this._objectTreeElement.expand(); | |
138 }, | |
139 | |
140 __proto__: TreeOutlineInShadow.prototype | |
141 }; | |
142 | |
143 /** | |
144 * @param {!WebInspector.RemoteObjectProperty} propertyA | |
145 * @param {!WebInspector.RemoteObjectProperty} propertyB | |
146 * @return {number} | |
147 */ | |
148 WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, pro
pertyB) | |
149 { | |
150 var a = propertyA.name; | 97 var a = propertyA.name; |
151 var b = propertyB.name; | 98 var b = propertyB.name; |
152 if (a === "__proto__") | 99 if (a === '__proto__') |
153 return 1; | 100 return 1; |
154 if (b === "__proto__") | 101 if (b === '__proto__') |
155 return -1; | 102 return -1; |
156 if (propertyA.symbol && !propertyB.symbol) | 103 if (propertyA.symbol && !propertyB.symbol) |
157 return 1; | 104 return 1; |
158 if (propertyB.symbol && !propertyA.symbol) | 105 if (propertyB.symbol && !propertyA.symbol) |
159 return -1; | 106 return -1; |
160 return String.naturalOrderComparator(a, b); | 107 return String.naturalOrderComparator(a, b); |
| 108 } |
| 109 |
| 110 /** |
| 111 * @param {?string} name |
| 112 * @return {!Element} |
| 113 */ |
| 114 static createNameElement(name) { |
| 115 var nameElement = createElementWithClass('span', 'name'); |
| 116 if (/^\s|\s$|^$|\n/.test(name)) |
| 117 nameElement.createTextChildren('"', name.replace(/\n/g, '\u21B5'), '"'); |
| 118 else |
| 119 nameElement.textContent = name; |
| 120 return nameElement; |
| 121 } |
| 122 |
| 123 /** |
| 124 * @param {?string=} description |
| 125 * @return {string} valueText |
| 126 */ |
| 127 static valueTextForFunctionDescription(description) { |
| 128 var text = description.replace(/^function [gs]et /, 'function '); |
| 129 var functionPrefixWithArguments = |
| 130 new RegExp(WebInspector.ObjectPropertiesSection._functionPrefixSource.so
urce + '([^)]*)'); |
| 131 var matches = functionPrefixWithArguments.exec(text); |
| 132 if (!matches) { |
| 133 // process shorthand methods |
| 134 matches = /[^(]*(\([^)]*)/.exec(text); |
| 135 } |
| 136 var match = matches ? matches[1] : null; |
| 137 return match ? match.replace(/\n/g, ' ') + ')' : (text || ''); |
| 138 } |
| 139 |
| 140 /** |
| 141 * @param {!WebInspector.RemoteObject} value |
| 142 * @param {boolean} wasThrown |
| 143 * @param {!Element=} parentElement |
| 144 * @param {!WebInspector.Linkifier=} linkifier |
| 145 * @return {!Element} |
| 146 */ |
| 147 static createValueElementWithCustomSupport(value, wasThrown, parentElement, li
nkifier) { |
| 148 if (value.customPreview()) { |
| 149 var result = (new WebInspector.CustomPreviewComponent(value)).element; |
| 150 result.classList.add('object-properties-section-custom-section'); |
| 151 return result; |
| 152 } |
| 153 return WebInspector.ObjectPropertiesSection.createValueElement(value, wasThr
own, parentElement, linkifier); |
| 154 } |
| 155 |
| 156 /** |
| 157 * @param {!WebInspector.RemoteObject} value |
| 158 * @param {boolean} wasThrown |
| 159 * @param {!Element=} parentElement |
| 160 * @param {!WebInspector.Linkifier=} linkifier |
| 161 * @return {!Element} |
| 162 */ |
| 163 static createValueElement(value, wasThrown, parentElement, linkifier) { |
| 164 var valueElement = createElementWithClass('span', 'value'); |
| 165 var type = value.type; |
| 166 var subtype = value.subtype; |
| 167 var description = value.description; |
| 168 var prefix; |
| 169 var valueText; |
| 170 var suffix; |
| 171 if (wasThrown) { |
| 172 prefix = '[Exception: '; |
| 173 valueText = description; |
| 174 suffix = ']'; |
| 175 } else if (type === 'string' && typeof description === 'string') { |
| 176 // Render \n as a nice unicode cr symbol. |
| 177 prefix = '"'; |
| 178 valueText = description.replace(/\n/g, '\u21B5'); |
| 179 suffix = '"'; |
| 180 } else if (type === 'function') { |
| 181 valueText = WebInspector.ObjectPropertiesSection.valueTextForFunctionDescr
iption(description); |
| 182 } else if (type !== 'object' || subtype !== 'node') { |
| 183 valueText = description; |
| 184 } |
| 185 if (type !== 'number' || valueText.indexOf('e') === -1) { |
| 186 valueElement.setTextContentTruncatedIfNeeded(valueText || ''); |
| 187 if (prefix) |
| 188 valueElement.insertBefore(createTextNode(prefix), valueElement.firstChil
d); |
| 189 if (suffix) |
| 190 valueElement.createTextChild(suffix); |
| 191 } else { |
| 192 var numberParts = valueText.split('e'); |
| 193 var mantissa = valueElement.createChild('span', 'object-value-scientific-n
otation-mantissa'); |
| 194 mantissa.textContent = numberParts[0]; |
| 195 var exponent = valueElement.createChild('span', 'object-value-scientific-n
otation-exponent'); |
| 196 exponent.textContent = 'e' + numberParts[1]; |
| 197 valueElement.classList.add('object-value-scientific-notation-number'); |
| 198 if (parentElement) // FIXME: do it in the caller. |
| 199 parentElement.classList.add('hbox'); |
| 200 } |
| 201 |
| 202 if (wasThrown) |
| 203 valueElement.classList.add('error'); |
| 204 if (subtype || type) |
| 205 valueElement.classList.add('object-value-' + (subtype || type)); |
| 206 |
| 207 if (type === 'object' && subtype === 'node' && description) { |
| 208 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(valueElement, de
scription); |
| 209 valueElement.addEventListener('click', mouseClick, false); |
| 210 valueElement.addEventListener('mousemove', mouseMove, false); |
| 211 valueElement.addEventListener('mouseleave', mouseLeave, false); |
| 212 } else { |
| 213 valueElement.title = description || ''; |
| 214 } |
| 215 |
| 216 if (type === 'object' && subtype === 'internal#location') { |
| 217 var rawLocation = value.debuggerModel().createRawLocationByScriptId( |
| 218 value.value.scriptId, value.value.lineNumber, value.value.columnNumber
); |
| 219 if (rawLocation && linkifier) |
| 220 return linkifier.linkifyRawLocation(rawLocation, ''); |
| 221 valueElement.textContent = '<unknown>'; |
| 222 } |
| 223 |
| 224 function mouseMove() { |
| 225 WebInspector.DOMModel.highlightObjectAsDOMNode(value); |
| 226 } |
| 227 |
| 228 function mouseLeave() { |
| 229 WebInspector.DOMModel.hideDOMNodeHighlight(); |
| 230 } |
| 231 |
| 232 /** |
| 233 * @param {!Event} event |
| 234 */ |
| 235 function mouseClick(event) { |
| 236 WebInspector.Revealer.reveal(value); |
| 237 event.consume(true); |
| 238 } |
| 239 |
| 240 return valueElement; |
| 241 } |
| 242 |
| 243 /** |
| 244 * @param {!WebInspector.RemoteObject} object |
| 245 * @return {boolean} |
| 246 */ |
| 247 static _needsAlternateTitle(object) { |
| 248 return object && object.hasChildren && !object.customPreview() && object.sub
type !== 'node' && |
| 249 object.type !== 'function' && (object.type !== 'object' || object.previe
w); |
| 250 } |
| 251 |
| 252 /** |
| 253 * @param {!WebInspector.RemoteObject} func |
| 254 * @param {!Element} element |
| 255 * @param {boolean} linkify |
| 256 * @param {boolean=} includePreview |
| 257 */ |
| 258 static formatObjectAsFunction(func, element, linkify, includePreview) { |
| 259 func.debuggerModel().functionDetailsPromise(func).then(didGetDetails); |
| 260 |
| 261 /** |
| 262 * @param {?WebInspector.DebuggerModel.FunctionDetails} response |
| 263 */ |
| 264 function didGetDetails(response) { |
| 265 if (!response) { |
| 266 var valueText = WebInspector.ObjectPropertiesSection.valueTextForFunctio
nDescription(func.description); |
| 267 element.createTextChild(valueText); |
| 268 return; |
| 269 } |
| 270 |
| 271 var matched = func.description.match(WebInspector.ObjectPropertiesSection.
_functionPrefixSource); |
| 272 if (matched) { |
| 273 var prefix = createElementWithClass('span', 'object-value-function-prefi
x'); |
| 274 prefix.textContent = matched[0]; |
| 275 element.appendChild(prefix); |
| 276 } |
| 277 |
| 278 if (linkify && response && response.location) { |
| 279 var anchor = createElement('span'); |
| 280 element.classList.add('linkified'); |
| 281 element.appendChild(anchor); |
| 282 element.addEventListener( |
| 283 'click', WebInspector.Revealer.reveal.bind(WebInspector.Revealer, re
sponse.location, undefined)); |
| 284 element = anchor; |
| 285 } |
| 286 |
| 287 var text = func.description.substring(0, 200); |
| 288 if (includePreview) { |
| 289 element.createTextChild( |
| 290 text.replace(WebInspector.ObjectPropertiesSection._functionPrefixSou
rce, '') + |
| 291 (func.description.length > 200 ? '\u2026' : '')); |
| 292 return; |
| 293 } |
| 294 |
| 295 // Now parse description and get the real params and title. |
| 296 self.runtime.extension(WebInspector.TokenizerFactory).instance().then(proc
essTokens); |
| 297 |
| 298 var params = null; |
| 299 var functionName = response ? response.functionName : ''; |
| 300 |
| 301 /** |
| 302 * @param {!WebInspector.TokenizerFactory} tokenizerFactory |
| 303 */ |
| 304 function processTokens(tokenizerFactory) { |
| 305 var tokenize = tokenizerFactory.createTokenizer('text/javascript'); |
| 306 tokenize(text, processToken); |
| 307 element.createTextChild((functionName || 'anonymous') + '(' + (params ||
[]).join(', ') + ')'); |
| 308 } |
| 309 |
| 310 var doneProcessing = false; |
| 311 |
| 312 /** |
| 313 * @param {string} token |
| 314 * @param {?string} tokenType |
| 315 * @param {number} column |
| 316 * @param {number} newColumn |
| 317 */ |
| 318 function processToken(token, tokenType, column, newColumn) { |
| 319 if (!params && tokenType === 'js-def' && !functionName) |
| 320 functionName = token; |
| 321 doneProcessing = doneProcessing || token === ')'; |
| 322 if (doneProcessing) |
| 323 return; |
| 324 if (token === '(') { |
| 325 params = []; |
| 326 return; |
| 327 } |
| 328 if (params && tokenType === 'js-def') |
| 329 params.push(token); |
| 330 } |
| 331 } |
| 332 } |
| 333 |
| 334 skipProto() { |
| 335 this._skipProto = true; |
| 336 } |
| 337 |
| 338 expand() { |
| 339 this._objectTreeElement.expand(); |
| 340 } |
| 341 |
| 342 /** |
| 343 * @param {boolean} value |
| 344 */ |
| 345 setEditable(value) { |
| 346 this._editable = value; |
| 347 } |
| 348 |
| 349 /** |
| 350 * @return {!TreeElement} |
| 351 */ |
| 352 objectTreeElement() { |
| 353 return this._objectTreeElement; |
| 354 } |
| 355 |
| 356 enableContextMenu() { |
| 357 this.element.addEventListener('contextmenu', this._contextMenuEventFired.bin
d(this), false); |
| 358 } |
| 359 |
| 360 _contextMenuEventFired(event) { |
| 361 var contextMenu = new WebInspector.ContextMenu(event); |
| 362 contextMenu.appendApplicableItems(this._object); |
| 363 contextMenu.show(); |
| 364 } |
| 365 |
| 366 titleLessMode() { |
| 367 this._objectTreeElement.listItemElement.classList.add('hidden'); |
| 368 this._objectTreeElement.childrenListElement.classList.add('title-less-mode')
; |
| 369 this._objectTreeElement.expand(); |
| 370 } |
161 }; | 371 }; |
162 | 372 |
| 373 /** @const */ |
| 374 WebInspector.ObjectPropertiesSection._arrayLoadThreshold = 100; |
| 375 |
| 376 |
163 /** | 377 /** |
164 * @constructor | 378 * @unrestricted |
165 * @extends {TreeElement} | |
166 * @param {!WebInspector.RemoteObject} object | |
167 * @param {!WebInspector.Linkifier=} linkifier | |
168 * @param {?string=} emptyPlaceholder | |
169 * @param {boolean=} ignoreHasOwnProperty | |
170 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties | |
171 */ | 379 */ |
172 WebInspector.ObjectPropertiesSection.RootElement = function(object, linkifier, e
mptyPlaceholder, ignoreHasOwnProperty, extraProperties) | 380 WebInspector.ObjectPropertiesSection.RootElement = class extends TreeElement { |
173 { | 381 /** |
174 var contentElement = createElement("content"); | 382 * @param {!WebInspector.RemoteObject} object |
175 TreeElement.call(this, contentElement); | 383 * @param {!WebInspector.Linkifier=} linkifier |
| 384 * @param {?string=} emptyPlaceholder |
| 385 * @param {boolean=} ignoreHasOwnProperty |
| 386 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties |
| 387 */ |
| 388 constructor(object, linkifier, emptyPlaceholder, ignoreHasOwnProperty, extraPr
operties) { |
| 389 var contentElement = createElement('content'); |
| 390 super(contentElement); |
176 | 391 |
177 this._object = object; | 392 this._object = object; |
178 this._extraProperties = extraProperties || []; | 393 this._extraProperties = extraProperties || []; |
179 this._ignoreHasOwnProperty = !!ignoreHasOwnProperty; | 394 this._ignoreHasOwnProperty = !!ignoreHasOwnProperty; |
180 this._emptyPlaceholder = emptyPlaceholder; | 395 this._emptyPlaceholder = emptyPlaceholder; |
181 | 396 |
182 this.setExpandable(true); | 397 this.setExpandable(true); |
183 this.selectable = false; | 398 this.selectable = false; |
184 this.toggleOnClick = true; | 399 this.toggleOnClick = true; |
185 this.listItemElement.classList.add("object-properties-section-root-element")
; | 400 this.listItemElement.classList.add('object-properties-section-root-element')
; |
186 this._linkifier = linkifier; | 401 this._linkifier = linkifier; |
187 }; | 402 } |
188 | 403 |
189 WebInspector.ObjectPropertiesSection.RootElement.prototype = { | 404 /** |
190 /** | 405 * @override |
191 * @override | 406 */ |
192 */ | 407 onexpand() { |
193 onexpand: function() | 408 if (this.treeOutline) { |
194 { | 409 this.treeOutline.element.classList.add('expanded'); |
195 if (this.treeOutline) { | 410 this._showExpandedTitleElement(true); |
196 this.treeOutline.element.classList.add("expanded"); | 411 } |
197 this._showExpandedTitleElement(true); | 412 } |
198 } | |
199 }, | |
200 | 413 |
201 /** | 414 /** |
202 * @override | 415 * @override |
203 */ | 416 */ |
204 oncollapse: function() | 417 oncollapse() { |
205 { | 418 if (this.treeOutline) { |
206 if (this.treeOutline) { | 419 this.treeOutline.element.classList.remove('expanded'); |
207 this.treeOutline.element.classList.remove("expanded"); | 420 this._showExpandedTitleElement(false); |
208 this._showExpandedTitleElement(false); | 421 } |
209 } | 422 } |
210 }, | |
211 | 423 |
212 /** | 424 /** |
213 * @param {boolean} value | 425 * @param {boolean} value |
214 */ | 426 */ |
215 _showExpandedTitleElement: function(value) | 427 _showExpandedTitleElement(value) { |
216 { | 428 if (!this.treeOutline.expandedTitleElement) |
217 if (!this.treeOutline.expandedTitleElement) | 429 return; |
218 return; | 430 if (value) |
219 if (value) | 431 this.treeOutline.element.replaceChild(this.treeOutline.expandedTitleElemen
t, this.treeOutline.titleElement); |
220 this.treeOutline.element.replaceChild(this.treeOutline.expandedTitle
Element, this.treeOutline.titleElement); | 432 else |
221 else | 433 this.treeOutline.element.replaceChild(this.treeOutline.titleElement, this.
treeOutline.expandedTitleElement); |
222 this.treeOutline.element.replaceChild(this.treeOutline.titleElement,
this.treeOutline.expandedTitleElement); | 434 } |
223 }, | |
224 | 435 |
225 /** | 436 /** |
226 * @override | 437 * @override |
227 * @param {!Event} e | 438 * @param {!Event} e |
228 * @return {boolean} | 439 * @return {boolean} |
229 */ | 440 */ |
230 ondblclick: function(e) | 441 ondblclick(e) { |
231 { | 442 return true; |
232 return true; | 443 } |
233 }, | |
234 | 444 |
235 onpopulate: function() | 445 /** |
236 { | 446 * @override |
237 WebInspector.ObjectPropertyTreeElement._populate(this, this._object, !!t
his.treeOutline._skipProto, this._linkifier, this._emptyPlaceholder, this._ignor
eHasOwnProperty, this._extraProperties); | 447 */ |
238 }, | 448 onpopulate() { |
239 | 449 WebInspector.ObjectPropertyTreeElement._populate( |
240 __proto__: TreeElement.prototype | 450 this, this._object, !!this.treeOutline._skipProto, this._linkifier, this
._emptyPlaceholder, |
| 451 this._ignoreHasOwnProperty, this._extraProperties); |
| 452 } |
241 }; | 453 }; |
242 | 454 |
243 /** | 455 /** |
244 * @constructor | 456 * @unrestricted |
245 * @extends {TreeElement} | |
246 * @param {!WebInspector.RemoteObjectProperty} property | |
247 * @param {!WebInspector.Linkifier=} linkifier | |
248 */ | 457 */ |
249 WebInspector.ObjectPropertyTreeElement = function(property, linkifier) | 458 WebInspector.ObjectPropertyTreeElement = class extends TreeElement { |
250 { | 459 /** |
| 460 * @param {!WebInspector.RemoteObjectProperty} property |
| 461 * @param {!WebInspector.Linkifier=} linkifier |
| 462 */ |
| 463 constructor(property, linkifier) { |
251 // Pass an empty title, the title gets made later in onattach. | 464 // Pass an empty title, the title gets made later in onattach. |
252 TreeElement.call(this); | 465 super(); |
253 | 466 |
254 this.property = property; | 467 this.property = property; |
255 this.toggleOnClick = true; | 468 this.toggleOnClick = true; |
256 this.selectable = false; | 469 this.selectable = false; |
257 /** @type {!Array.<!Object>} */ | 470 /** @type {!Array.<!Object>} */ |
258 this._highlightChanges = []; | 471 this._highlightChanges = []; |
259 this._linkifier = linkifier; | 472 this._linkifier = linkifier; |
260 }; | 473 } |
261 | 474 |
262 WebInspector.ObjectPropertyTreeElement.prototype = { | 475 /** |
263 /** | 476 * @param {!TreeElement} treeElement |
264 * @param {!RegExp} regex | 477 * @param {!WebInspector.RemoteObject} value |
265 * @param {string=} additionalCssClassName | 478 * @param {boolean} skipProto |
266 * @return {boolean} | 479 * @param {!WebInspector.Linkifier=} linkifier |
267 */ | 480 * @param {?string=} emptyPlaceholder |
268 setSearchRegex: function(regex, additionalCssClassName) | 481 * @param {boolean=} flattenProtoChain |
269 { | 482 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties |
270 var cssClasses = WebInspector.highlightedSearchResultClassName; | 483 * @param {!WebInspector.RemoteObject=} targetValue |
271 if (additionalCssClassName) | 484 */ |
272 cssClasses += " " + additionalCssClassName; | 485 static _populate( |
273 this.revertHighlightChanges(); | 486 treeElement, |
274 | 487 value, |
275 this._applySearch(regex, this.nameElement, cssClasses); | 488 skipProto, |
276 var valueType = this.property.value.type; | 489 linkifier, |
277 if (valueType !== "object") | 490 emptyPlaceholder, |
278 this._applySearch(regex, this.valueElement, cssClasses); | 491 flattenProtoChain, |
279 | 492 extraProperties, |
280 return !!this._highlightChanges.length; | 493 targetValue) { |
281 }, | |
282 | |
283 /** | |
284 * @param {!RegExp} regex | |
285 * @param {!Element} element | |
286 * @param {string} cssClassName | |
287 */ | |
288 _applySearch: function(regex, element, cssClassName) | |
289 { | |
290 var ranges = []; | |
291 var content = element.textContent; | |
292 regex.lastIndex = 0; | |
293 var match = regex.exec(content); | |
294 while (match) { | |
295 ranges.push(new WebInspector.SourceRange(match.index, match[0].lengt
h)); | |
296 match = regex.exec(content); | |
297 } | |
298 if (ranges.length) | |
299 WebInspector.highlightRangesWithStyleClass(element, ranges, cssClass
Name, this._highlightChanges); | |
300 }, | |
301 | |
302 revertHighlightChanges: function() | |
303 { | |
304 WebInspector.revertDomChanges(this._highlightChanges); | |
305 this._highlightChanges = []; | |
306 }, | |
307 | |
308 onpopulate: function() | |
309 { | |
310 var propertyValue = /** @type {!WebInspector.RemoteObject} */ (this.prop
erty.value); | |
311 console.assert(propertyValue); | |
312 var skipProto = this.treeOutline ? this.treeOutline._skipProto : true; | |
313 var targetValue = this.property.name !== "__proto__" ? propertyValue : t
his.property.parentObject; | |
314 WebInspector.ObjectPropertyTreeElement._populate(this, propertyValue, sk
ipProto, this._linkifier, undefined, undefined, undefined, targetValue); | |
315 }, | |
316 | |
317 /** | |
318 * @override | |
319 * @return {boolean} | |
320 */ | |
321 ondblclick: function(event) | |
322 { | |
323 var inEditableElement = event.target.isSelfOrDescendant(this.valueElemen
t) || (this.expandedValueElement && event.target.isSelfOrDescendant(this.expande
dValueElement)); | |
324 if (!this.property.value.customPreview() && inEditableElement && (this.p
roperty.writable || this.property.setter)) | |
325 this._startEditing(); | |
326 return false; | |
327 }, | |
328 | |
329 /** | |
330 * @override | |
331 */ | |
332 onattach: function() | |
333 { | |
334 this.update(); | |
335 this._updateExpandable(); | |
336 }, | |
337 | |
338 /** | |
339 * @override | |
340 */ | |
341 onexpand: function() | |
342 { | |
343 this._showExpandedValueElement(true); | |
344 }, | |
345 | |
346 /** | |
347 * @override | |
348 */ | |
349 oncollapse: function() | |
350 { | |
351 this._showExpandedValueElement(false); | |
352 }, | |
353 | |
354 /** | |
355 * @param {boolean} value | |
356 */ | |
357 _showExpandedValueElement: function(value) | |
358 { | |
359 if (!this.expandedValueElement) | |
360 return; | |
361 if (value) | |
362 this.listItemElement.replaceChild(this.expandedValueElement, this.va
lueElement); | |
363 else | |
364 this.listItemElement.replaceChild(this.valueElement, this.expandedVa
lueElement); | |
365 }, | |
366 | |
367 /** | |
368 * @param {!WebInspector.RemoteObject} value | |
369 * @return {?Element} | |
370 */ | |
371 _createExpandedValueElement: function(value) | |
372 { | |
373 if (!WebInspector.ObjectPropertiesSection._needsAlternateTitle(value)) | |
374 return null; | |
375 | |
376 var valueElement = createElementWithClass("span", "value"); | |
377 valueElement.setTextContentTruncatedIfNeeded(value.description || ""); | |
378 valueElement.classList.add("object-value-" + (value.subtype || value.typ
e)); | |
379 valueElement.title = value.description || ""; | |
380 return valueElement; | |
381 }, | |
382 | |
383 update: function() | |
384 { | |
385 this.nameElement = WebInspector.ObjectPropertiesSection.createNameElemen
t(this.property.name); | |
386 if (!this.property.enumerable) | |
387 this.nameElement.classList.add("object-properties-section-dimmed"); | |
388 if (this.property.synthetic) | |
389 this.nameElement.classList.add("synthetic-property"); | |
390 | |
391 this._updatePropertyPath(); | |
392 this.nameElement.addEventListener("contextmenu", this._contextMenuFired.
bind(this, this.property), false); | |
393 | |
394 var separatorElement = createElementWithClass("span", "object-properties
-section-separator"); | |
395 separatorElement.textContent = ": "; | |
396 | |
397 if (this.property.value) { | |
398 this.valueElement = WebInspector.ObjectPropertiesSection.createValue
ElementWithCustomSupport(this.property.value, this.property.wasThrown, this.list
ItemElement, this._linkifier); | |
399 this.valueElement.addEventListener("contextmenu", this._contextMenuF
ired.bind(this, this.property), false); | |
400 } else if (this.property.getter) { | |
401 this.valueElement = WebInspector.ObjectPropertyTreeElement.createRem
oteObjectAccessorPropertySpan(this.property.parentObject, [this.property.name],
this._onInvokeGetterClick.bind(this)); | |
402 } else { | |
403 this.valueElement = createElementWithClass("span", "object-value-und
efined"); | |
404 this.valueElement.textContent = WebInspector.UIString("<unreadable>"
); | |
405 this.valueElement.title = WebInspector.UIString("No property getter"
); | |
406 } | |
407 | |
408 var valueText = this.valueElement.textContent; | |
409 if (this.property.value && valueText && !this.property.wasThrown) | |
410 this.expandedValueElement = this._createExpandedValueElement(this.pr
operty.value); | |
411 | |
412 this.listItemElement.removeChildren(); | |
413 this.listItemElement.appendChildren(this.nameElement, separatorElement,
this.valueElement); | |
414 }, | |
415 | |
416 _updatePropertyPath: function() | |
417 { | |
418 if (this.nameElement.title) | |
419 return; | |
420 | |
421 var useDotNotation = /^(_|\$|[A-Z])(_|\$|[A-Z]|\d)*$/i; | |
422 var isInteger = /^[1-9]\d*$/; | |
423 var name = this.property.name; | |
424 var parentPath = this.parent.nameElement ? this.parent.nameElement.title
: ""; | |
425 if (useDotNotation.test(name)) | |
426 this.nameElement.title = parentPath + "." + name; | |
427 else if (isInteger.test(name)) | |
428 this.nameElement.title = parentPath + "[" + name + "]"; | |
429 else | |
430 this.nameElement.title = parentPath + "[\"" + name + "\"]"; | |
431 }, | |
432 | |
433 /** | |
434 * @param {!WebInspector.RemoteObjectProperty} property | |
435 * @param {!Event} event | |
436 */ | |
437 _contextMenuFired: function(property, event) | |
438 { | |
439 var contextMenu = new WebInspector.ContextMenu(event); | |
440 if (property.symbol) | |
441 contextMenu.appendApplicableItems(property.symbol); | |
442 if (property.value) | |
443 contextMenu.appendApplicableItems(property.value); | |
444 var copyPathHandler = InspectorFrontendHost.copyText.bind(InspectorFront
endHost, this.nameElement.title); | |
445 contextMenu.beforeShow(() => { contextMenu.appendItem(WebInspector.UIStr
ing.capitalize("Copy ^property ^path"), copyPathHandler); }); | |
446 contextMenu.show(); | |
447 }, | |
448 | |
449 _startEditing: function() | |
450 { | |
451 if (this._prompt || !this.treeOutline._editable || this._readOnly) | |
452 return; | |
453 | |
454 this._editableDiv = this.listItemElement.createChild("span"); | |
455 | |
456 var text = this.property.value.description; | |
457 if (this.property.value.type === "string" && typeof text === "string") | |
458 text = "\"" + text + "\""; | |
459 | |
460 this._editableDiv.setTextContentTruncatedIfNeeded(text, WebInspector.UIS
tring("<string is too large to edit>")); | |
461 var originalContent = this._editableDiv.textContent; | |
462 | |
463 // Lie about our children to prevent expanding on double click and to co
llapse subproperties. | |
464 this.setExpandable(false); | |
465 this.listItemElement.classList.add("editing-sub-part"); | |
466 this.valueElement.classList.add("hidden"); | |
467 | |
468 this._prompt = new WebInspector.ObjectPropertyPrompt(); | |
469 | |
470 var proxyElement = this._prompt.attachAndStartEditing(this._editableDiv,
this._editingCommitted.bind(this, originalContent)); | |
471 this.listItemElement.getComponentSelection().setBaseAndExtent(this._edit
ableDiv, 0, this._editableDiv, 1); | |
472 proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this,
originalContent), false); | |
473 }, | |
474 | |
475 _editingEnded: function() | |
476 { | |
477 this._prompt.detach(); | |
478 delete this._prompt; | |
479 this._editableDiv.remove(); | |
480 this._updateExpandable(); | |
481 this.listItemElement.scrollLeft = 0; | |
482 this.listItemElement.classList.remove("editing-sub-part"); | |
483 }, | |
484 | |
485 _editingCancelled: function() | |
486 { | |
487 this.valueElement.classList.remove("hidden"); | |
488 this._editingEnded(); | |
489 }, | |
490 | |
491 /** | |
492 * @param {string} originalContent | |
493 */ | |
494 _editingCommitted: function(originalContent) | |
495 { | |
496 var userInput = this._prompt.text(); | |
497 if (userInput === originalContent) { | |
498 this._editingCancelled(); // nothing changed, so cancel | |
499 return; | |
500 } | |
501 | |
502 this._editingEnded(); | |
503 this._applyExpression(userInput); | |
504 }, | |
505 | |
506 /** | |
507 * @param {string} originalContent | |
508 * @param {!Event} event | |
509 */ | |
510 _promptKeyDown: function(originalContent, event) | |
511 { | |
512 if (isEnterKey(event)) { | |
513 event.consume(true); | |
514 this._editingCommitted(originalContent); | |
515 return; | |
516 } | |
517 if (event.key === "Escape") { | |
518 event.consume(); | |
519 this._editingCancelled(); | |
520 return; | |
521 } | |
522 }, | |
523 | |
524 /** | |
525 * @param {string} expression | |
526 */ | |
527 _applyExpression: function(expression) | |
528 { | |
529 var property = WebInspector.RemoteObject.toCallArgument(this.property.sy
mbol || this.property.name); | |
530 expression = expression.trim(); | |
531 if (expression) | |
532 this.property.parentObject.setPropertyValue(property, expression, ca
llback.bind(this)); | |
533 else | |
534 this.property.parentObject.deleteProperty(property, callback.bind(th
is)); | |
535 | |
536 /** | |
537 * @param {?Protocol.Error} error | |
538 * @this {WebInspector.ObjectPropertyTreeElement} | |
539 */ | |
540 function callback(error) | |
541 { | |
542 if (error) { | |
543 this.update(); | |
544 return; | |
545 } | |
546 | |
547 if (!expression) { | |
548 // The property was deleted, so remove this tree element. | |
549 this.parent.removeChild(this); | |
550 } else { | |
551 // Call updateSiblings since their value might be based on the v
alue that just changed. | |
552 var parent = this.parent; | |
553 parent.invalidateChildren(); | |
554 parent.onpopulate(); | |
555 } | |
556 } | |
557 }, | |
558 | |
559 /** | |
560 * @param {?WebInspector.RemoteObject} result | |
561 * @param {boolean=} wasThrown | |
562 */ | |
563 _onInvokeGetterClick: function(result, wasThrown) | |
564 { | |
565 if (!result) | |
566 return; | |
567 this.property.value = result; | |
568 this.property.wasThrown = wasThrown; | |
569 | |
570 this.update(); | |
571 this.invalidateChildren(); | |
572 this._updateExpandable(); | |
573 }, | |
574 | |
575 _updateExpandable: function() | |
576 { | |
577 if (this.property.value) | |
578 this.setExpandable(!this.property.value.customPreview() && this.prop
erty.value.hasChildren && !this.property.wasThrown); | |
579 else | |
580 this.setExpandable(false); | |
581 }, | |
582 | |
583 __proto__: TreeElement.prototype | |
584 }; | |
585 | |
586 /** | |
587 * @param {!TreeElement} treeElement | |
588 * @param {!WebInspector.RemoteObject} value | |
589 * @param {boolean} skipProto | |
590 * @param {!WebInspector.Linkifier=} linkifier | |
591 * @param {?string=} emptyPlaceholder | |
592 * @param {boolean=} flattenProtoChain | |
593 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties | |
594 * @param {!WebInspector.RemoteObject=} targetValue | |
595 */ | |
596 WebInspector.ObjectPropertyTreeElement._populate = function(treeElement, value,
skipProto, linkifier, emptyPlaceholder, flattenProtoChain, extraProperties, targ
etValue) | |
597 { | |
598 if (value.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThr
eshold) { | 494 if (value.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThr
eshold) { |
599 treeElement.removeChildren(); | 495 treeElement.removeChildren(); |
600 WebInspector.ArrayGroupingTreeElement._populateArray(treeElement, value,
0, value.arrayLength() - 1, linkifier); | 496 WebInspector.ArrayGroupingTreeElement._populateArray(treeElement, value, 0
, value.arrayLength() - 1, linkifier); |
601 return; | 497 return; |
602 } | 498 } |
603 | 499 |
604 /** | 500 /** |
605 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties | 501 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties |
606 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties | 502 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties |
607 */ | 503 */ |
608 function callback(properties, internalProperties) | 504 function callback(properties, internalProperties) { |
609 { | 505 treeElement.removeChildren(); |
610 treeElement.removeChildren(); | 506 if (!properties) |
611 if (!properties) | 507 return; |
612 return; | 508 |
613 | 509 extraProperties = extraProperties || []; |
614 extraProperties = extraProperties || []; | 510 for (var i = 0; i < extraProperties.length; ++i) |
615 for (var i = 0; i < extraProperties.length; ++i) | 511 properties.push(extraProperties[i]); |
616 properties.push(extraProperties[i]); | 512 |
617 | 513 WebInspector.ObjectPropertyTreeElement.populateWithProperties( |
618 WebInspector.ObjectPropertyTreeElement.populateWithProperties(treeElemen
t, properties, internalProperties, | 514 treeElement, properties, internalProperties, skipProto, targetValue ||
value, linkifier, emptyPlaceholder); |
619 skipProto, targetValue || value, linkifier, emptyPlaceholder); | |
620 } | 515 } |
621 | 516 |
622 if (flattenProtoChain) | 517 if (flattenProtoChain) |
623 value.getAllProperties(false, callback); | 518 value.getAllProperties(false, callback); |
624 else | 519 else |
625 WebInspector.RemoteObject.loadFromObjectPerProto(value, callback); | 520 WebInspector.RemoteObject.loadFromObjectPerProto(value, callback); |
626 }; | 521 } |
627 | 522 |
628 /** | 523 /** |
629 * @param {!TreeElement} treeNode | 524 * @param {!TreeElement} treeNode |
630 * @param {!Array.<!WebInspector.RemoteObjectProperty>} properties | 525 * @param {!Array.<!WebInspector.RemoteObjectProperty>} properties |
631 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties | 526 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties |
632 * @param {boolean} skipProto | 527 * @param {boolean} skipProto |
633 * @param {?WebInspector.RemoteObject} value | 528 * @param {?WebInspector.RemoteObject} value |
634 * @param {!WebInspector.Linkifier=} linkifier | 529 * @param {!WebInspector.Linkifier=} linkifier |
635 * @param {?string=} emptyPlaceholder | 530 * @param {?string=} emptyPlaceholder |
636 */ | 531 */ |
637 WebInspector.ObjectPropertyTreeElement.populateWithProperties = function(treeNod
e, properties, internalProperties, skipProto, value, linkifier, emptyPlaceholder
) { | 532 static populateWithProperties( |
| 533 treeNode, |
| 534 properties, |
| 535 internalProperties, |
| 536 skipProto, |
| 537 value, |
| 538 linkifier, |
| 539 emptyPlaceholder) { |
638 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); | 540 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); |
639 | 541 |
640 var tailProperties = []; | 542 var tailProperties = []; |
641 var protoProperty = null; | 543 var protoProperty = null; |
642 for (var i = 0; i < properties.length; ++i) { | 544 for (var i = 0; i < properties.length; ++i) { |
643 var property = properties[i]; | 545 var property = properties[i]; |
644 property.parentObject = value; | 546 property.parentObject = value; |
645 if (property.name === "__proto__" && !property.isAccessorProperty()) { | 547 if (property.name === '__proto__' && !property.isAccessorProperty()) { |
646 protoProperty = property; | 548 protoProperty = property; |
647 continue; | 549 continue; |
| 550 } |
| 551 |
| 552 if (property.isOwn && property.getter) { |
| 553 var getterProperty = new WebInspector.RemoteObjectProperty('get ' + prop
erty.name, property.getter, false); |
| 554 getterProperty.parentObject = value; |
| 555 tailProperties.push(getterProperty); |
| 556 } |
| 557 if (property.isOwn && property.setter) { |
| 558 var setterProperty = new WebInspector.RemoteObjectProperty('set ' + prop
erty.name, property.setter, false); |
| 559 setterProperty.parentObject = value; |
| 560 tailProperties.push(setterProperty); |
| 561 } |
| 562 var canShowProperty = property.getter || !property.isAccessorProperty(); |
| 563 if (canShowProperty && property.name !== '__proto__') |
| 564 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(property
, linkifier)); |
| 565 } |
| 566 for (var i = 0; i < tailProperties.length; ++i) |
| 567 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(tailProper
ties[i], linkifier)); |
| 568 if (!skipProto && protoProperty) |
| 569 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(protoPrope
rty, linkifier)); |
| 570 |
| 571 if (internalProperties) { |
| 572 for (var i = 0; i < internalProperties.length; i++) { |
| 573 internalProperties[i].parentObject = value; |
| 574 var treeElement = new WebInspector.ObjectPropertyTreeElement(internalPro
perties[i], linkifier); |
| 575 if (internalProperties[i].name === '[[Entries]]') { |
| 576 treeElement.setExpandable(true); |
| 577 treeElement.expand(); |
648 } | 578 } |
649 | 579 treeNode.appendChild(treeElement); |
650 if (property.isOwn && property.getter) { | 580 } |
651 var getterProperty = new WebInspector.RemoteObjectProperty("get " +
property.name, property.getter, false); | |
652 getterProperty.parentObject = value; | |
653 tailProperties.push(getterProperty); | |
654 } | |
655 if (property.isOwn && property.setter) { | |
656 var setterProperty = new WebInspector.RemoteObjectProperty("set " +
property.name, property.setter, false); | |
657 setterProperty.parentObject = value; | |
658 tailProperties.push(setterProperty); | |
659 } | |
660 var canShowProperty = property.getter || !property.isAccessorProperty(); | |
661 if (canShowProperty && property.name !== "__proto__") | |
662 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(prop
erty, linkifier)); | |
663 } | |
664 for (var i = 0; i < tailProperties.length; ++i) | |
665 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(tailProp
erties[i], linkifier)); | |
666 if (!skipProto && protoProperty) | |
667 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(protoPro
perty, linkifier)); | |
668 | |
669 if (internalProperties) { | |
670 for (var i = 0; i < internalProperties.length; i++) { | |
671 internalProperties[i].parentObject = value; | |
672 var treeElement = new WebInspector.ObjectPropertyTreeElement(interna
lProperties[i], linkifier); | |
673 if (internalProperties[i].name === "[[Entries]]") { | |
674 treeElement.setExpandable(true); | |
675 treeElement.expand(); | |
676 } | |
677 treeNode.appendChild(treeElement); | |
678 } | |
679 } | 581 } |
680 | 582 |
681 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded(treeN
ode, emptyPlaceholder); | 583 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded(treeN
ode, emptyPlaceholder); |
682 }; | 584 } |
683 | 585 |
684 /** | 586 /** |
685 * @param {!TreeElement} treeNode | 587 * @param {!TreeElement} treeNode |
686 * @param {?string=} emptyPlaceholder | 588 * @param {?string=} emptyPlaceholder |
687 */ | 589 */ |
688 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded = functio
n(treeNode, emptyPlaceholder) | 590 static _appendEmptyPlaceholderIfNeeded(treeNode, emptyPlaceholder) { |
689 { | |
690 if (treeNode.childCount()) | 591 if (treeNode.childCount()) |
691 return; | 592 return; |
692 var title = createElementWithClass("div", "gray-info-message"); | 593 var title = createElementWithClass('div', 'gray-info-message'); |
693 title.textContent = emptyPlaceholder || WebInspector.UIString("No Properties
"); | 594 title.textContent = emptyPlaceholder || WebInspector.UIString('No Properties
'); |
694 var infoElement = new TreeElement(title); | 595 var infoElement = new TreeElement(title); |
695 treeNode.appendChild(infoElement); | 596 treeNode.appendChild(infoElement); |
| 597 } |
| 598 |
| 599 /** |
| 600 * @param {?WebInspector.RemoteObject} object |
| 601 * @param {!Array.<string>} propertyPath |
| 602 * @param {function(?WebInspector.RemoteObject, boolean=)} callback |
| 603 * @return {!Element} |
| 604 */ |
| 605 static createRemoteObjectAccessorPropertySpan(object, propertyPath, callback)
{ |
| 606 var rootElement = createElement('span'); |
| 607 var element = rootElement.createChild('span'); |
| 608 element.textContent = WebInspector.UIString('(...)'); |
| 609 if (!object) |
| 610 return rootElement; |
| 611 element.classList.add('object-value-calculate-value-button'); |
| 612 element.title = WebInspector.UIString('Invoke property getter'); |
| 613 element.addEventListener('click', onInvokeGetterClick, false); |
| 614 |
| 615 function onInvokeGetterClick(event) { |
| 616 event.consume(); |
| 617 object.getProperty(propertyPath, callback); |
| 618 } |
| 619 |
| 620 return rootElement; |
| 621 } |
| 622 |
| 623 /** |
| 624 * @param {!RegExp} regex |
| 625 * @param {string=} additionalCssClassName |
| 626 * @return {boolean} |
| 627 */ |
| 628 setSearchRegex(regex, additionalCssClassName) { |
| 629 var cssClasses = WebInspector.highlightedSearchResultClassName; |
| 630 if (additionalCssClassName) |
| 631 cssClasses += ' ' + additionalCssClassName; |
| 632 this.revertHighlightChanges(); |
| 633 |
| 634 this._applySearch(regex, this.nameElement, cssClasses); |
| 635 var valueType = this.property.value.type; |
| 636 if (valueType !== 'object') |
| 637 this._applySearch(regex, this.valueElement, cssClasses); |
| 638 |
| 639 return !!this._highlightChanges.length; |
| 640 } |
| 641 |
| 642 /** |
| 643 * @param {!RegExp} regex |
| 644 * @param {!Element} element |
| 645 * @param {string} cssClassName |
| 646 */ |
| 647 _applySearch(regex, element, cssClassName) { |
| 648 var ranges = []; |
| 649 var content = element.textContent; |
| 650 regex.lastIndex = 0; |
| 651 var match = regex.exec(content); |
| 652 while (match) { |
| 653 ranges.push(new WebInspector.SourceRange(match.index, match[0].length)); |
| 654 match = regex.exec(content); |
| 655 } |
| 656 if (ranges.length) |
| 657 WebInspector.highlightRangesWithStyleClass(element, ranges, cssClassName,
this._highlightChanges); |
| 658 } |
| 659 |
| 660 revertHighlightChanges() { |
| 661 WebInspector.revertDomChanges(this._highlightChanges); |
| 662 this._highlightChanges = []; |
| 663 } |
| 664 |
| 665 /** |
| 666 * @override |
| 667 */ |
| 668 onpopulate() { |
| 669 var propertyValue = /** @type {!WebInspector.RemoteObject} */ (this.property
.value); |
| 670 console.assert(propertyValue); |
| 671 var skipProto = this.treeOutline ? this.treeOutline._skipProto : true; |
| 672 var targetValue = this.property.name !== '__proto__' ? propertyValue : this.
property.parentObject; |
| 673 WebInspector.ObjectPropertyTreeElement._populate( |
| 674 this, propertyValue, skipProto, this._linkifier, undefined, undefined, u
ndefined, targetValue); |
| 675 } |
| 676 |
| 677 /** |
| 678 * @override |
| 679 * @return {boolean} |
| 680 */ |
| 681 ondblclick(event) { |
| 682 var inEditableElement = event.target.isSelfOrDescendant(this.valueElement) |
| |
| 683 (this.expandedValueElement && event.target.isSelfOrDescendant(this.expan
dedValueElement)); |
| 684 if (!this.property.value.customPreview() && inEditableElement && (this.prope
rty.writable || this.property.setter)) |
| 685 this._startEditing(); |
| 686 return false; |
| 687 } |
| 688 |
| 689 /** |
| 690 * @override |
| 691 */ |
| 692 onattach() { |
| 693 this.update(); |
| 694 this._updateExpandable(); |
| 695 } |
| 696 |
| 697 /** |
| 698 * @override |
| 699 */ |
| 700 onexpand() { |
| 701 this._showExpandedValueElement(true); |
| 702 } |
| 703 |
| 704 /** |
| 705 * @override |
| 706 */ |
| 707 oncollapse() { |
| 708 this._showExpandedValueElement(false); |
| 709 } |
| 710 |
| 711 /** |
| 712 * @param {boolean} value |
| 713 */ |
| 714 _showExpandedValueElement(value) { |
| 715 if (!this.expandedValueElement) |
| 716 return; |
| 717 if (value) |
| 718 this.listItemElement.replaceChild(this.expandedValueElement, this.valueEle
ment); |
| 719 else |
| 720 this.listItemElement.replaceChild(this.valueElement, this.expandedValueEle
ment); |
| 721 } |
| 722 |
| 723 /** |
| 724 * @param {!WebInspector.RemoteObject} value |
| 725 * @return {?Element} |
| 726 */ |
| 727 _createExpandedValueElement(value) { |
| 728 if (!WebInspector.ObjectPropertiesSection._needsAlternateTitle(value)) |
| 729 return null; |
| 730 |
| 731 var valueElement = createElementWithClass('span', 'value'); |
| 732 valueElement.setTextContentTruncatedIfNeeded(value.description || ''); |
| 733 valueElement.classList.add('object-value-' + (value.subtype || value.type)); |
| 734 valueElement.title = value.description || ''; |
| 735 return valueElement; |
| 736 } |
| 737 |
| 738 update() { |
| 739 this.nameElement = WebInspector.ObjectPropertiesSection.createNameElement(th
is.property.name); |
| 740 if (!this.property.enumerable) |
| 741 this.nameElement.classList.add('object-properties-section-dimmed'); |
| 742 if (this.property.synthetic) |
| 743 this.nameElement.classList.add('synthetic-property'); |
| 744 |
| 745 this._updatePropertyPath(); |
| 746 this.nameElement.addEventListener('contextmenu', this._contextMenuFired.bind
(this, this.property), false); |
| 747 |
| 748 var separatorElement = createElementWithClass('span', 'object-properties-sec
tion-separator'); |
| 749 separatorElement.textContent = ': '; |
| 750 |
| 751 if (this.property.value) { |
| 752 this.valueElement = WebInspector.ObjectPropertiesSection.createValueElemen
tWithCustomSupport( |
| 753 this.property.value, this.property.wasThrown, this.listItemElement, th
is._linkifier); |
| 754 this.valueElement.addEventListener('contextmenu', this._contextMenuFired.b
ind(this, this.property), false); |
| 755 } else if (this.property.getter) { |
| 756 this.valueElement = WebInspector.ObjectPropertyTreeElement.createRemoteObj
ectAccessorPropertySpan( |
| 757 this.property.parentObject, [this.property.name], this._onInvokeGetter
Click.bind(this)); |
| 758 } else { |
| 759 this.valueElement = createElementWithClass('span', 'object-value-undefined
'); |
| 760 this.valueElement.textContent = WebInspector.UIString('<unreadable>'); |
| 761 this.valueElement.title = WebInspector.UIString('No property getter'); |
| 762 } |
| 763 |
| 764 var valueText = this.valueElement.textContent; |
| 765 if (this.property.value && valueText && !this.property.wasThrown) |
| 766 this.expandedValueElement = this._createExpandedValueElement(this.property
.value); |
| 767 |
| 768 this.listItemElement.removeChildren(); |
| 769 this.listItemElement.appendChildren(this.nameElement, separatorElement, this
.valueElement); |
| 770 } |
| 771 |
| 772 _updatePropertyPath() { |
| 773 if (this.nameElement.title) |
| 774 return; |
| 775 |
| 776 var useDotNotation = /^(_|\$|[A-Z])(_|\$|[A-Z]|\d)*$/i; |
| 777 var isInteger = /^[1-9]\d*$/; |
| 778 var name = this.property.name; |
| 779 var parentPath = this.parent.nameElement ? this.parent.nameElement.title : '
'; |
| 780 if (useDotNotation.test(name)) |
| 781 this.nameElement.title = parentPath + '.' + name; |
| 782 else if (isInteger.test(name)) |
| 783 this.nameElement.title = parentPath + '[' + name + ']'; |
| 784 else |
| 785 this.nameElement.title = parentPath + '["' + name + '"]'; |
| 786 } |
| 787 |
| 788 /** |
| 789 * @param {!WebInspector.RemoteObjectProperty} property |
| 790 * @param {!Event} event |
| 791 */ |
| 792 _contextMenuFired(property, event) { |
| 793 var contextMenu = new WebInspector.ContextMenu(event); |
| 794 if (property.symbol) |
| 795 contextMenu.appendApplicableItems(property.symbol); |
| 796 if (property.value) |
| 797 contextMenu.appendApplicableItems(property.value); |
| 798 var copyPathHandler = InspectorFrontendHost.copyText.bind(InspectorFrontendH
ost, this.nameElement.title); |
| 799 contextMenu.beforeShow(() => { |
| 800 contextMenu.appendItem(WebInspector.UIString.capitalize('Copy ^property ^p
ath'), copyPathHandler); |
| 801 }); |
| 802 contextMenu.show(); |
| 803 } |
| 804 |
| 805 _startEditing() { |
| 806 if (this._prompt || !this.treeOutline._editable || this._readOnly) |
| 807 return; |
| 808 |
| 809 this._editableDiv = this.listItemElement.createChild('span'); |
| 810 |
| 811 var text = this.property.value.description; |
| 812 if (this.property.value.type === 'string' && typeof text === 'string') |
| 813 text = '"' + text + '"'; |
| 814 |
| 815 this._editableDiv.setTextContentTruncatedIfNeeded(text, WebInspector.UIStrin
g('<string is too large to edit>')); |
| 816 var originalContent = this._editableDiv.textContent; |
| 817 |
| 818 // Lie about our children to prevent expanding on double click and to collap
se subproperties. |
| 819 this.setExpandable(false); |
| 820 this.listItemElement.classList.add('editing-sub-part'); |
| 821 this.valueElement.classList.add('hidden'); |
| 822 |
| 823 this._prompt = new WebInspector.ObjectPropertyPrompt(); |
| 824 |
| 825 var proxyElement = |
| 826 this._prompt.attachAndStartEditing(this._editableDiv, this._editingCommi
tted.bind(this, originalContent)); |
| 827 this.listItemElement.getComponentSelection().setBaseAndExtent(this._editable
Div, 0, this._editableDiv, 1); |
| 828 proxyElement.addEventListener('keydown', this._promptKeyDown.bind(this, orig
inalContent), false); |
| 829 } |
| 830 |
| 831 _editingEnded() { |
| 832 this._prompt.detach(); |
| 833 delete this._prompt; |
| 834 this._editableDiv.remove(); |
| 835 this._updateExpandable(); |
| 836 this.listItemElement.scrollLeft = 0; |
| 837 this.listItemElement.classList.remove('editing-sub-part'); |
| 838 } |
| 839 |
| 840 _editingCancelled() { |
| 841 this.valueElement.classList.remove('hidden'); |
| 842 this._editingEnded(); |
| 843 } |
| 844 |
| 845 /** |
| 846 * @param {string} originalContent |
| 847 */ |
| 848 _editingCommitted(originalContent) { |
| 849 var userInput = this._prompt.text(); |
| 850 if (userInput === originalContent) { |
| 851 this._editingCancelled(); // nothing changed, so cancel |
| 852 return; |
| 853 } |
| 854 |
| 855 this._editingEnded(); |
| 856 this._applyExpression(userInput); |
| 857 } |
| 858 |
| 859 /** |
| 860 * @param {string} originalContent |
| 861 * @param {!Event} event |
| 862 */ |
| 863 _promptKeyDown(originalContent, event) { |
| 864 if (isEnterKey(event)) { |
| 865 event.consume(true); |
| 866 this._editingCommitted(originalContent); |
| 867 return; |
| 868 } |
| 869 if (event.key === 'Escape') { |
| 870 event.consume(); |
| 871 this._editingCancelled(); |
| 872 return; |
| 873 } |
| 874 } |
| 875 |
| 876 /** |
| 877 * @param {string} expression |
| 878 */ |
| 879 _applyExpression(expression) { |
| 880 var property = WebInspector.RemoteObject.toCallArgument(this.property.symbol
|| this.property.name); |
| 881 expression = expression.trim(); |
| 882 if (expression) |
| 883 this.property.parentObject.setPropertyValue(property, expression, callback
.bind(this)); |
| 884 else |
| 885 this.property.parentObject.deleteProperty(property, callback.bind(this)); |
| 886 |
| 887 /** |
| 888 * @param {?Protocol.Error} error |
| 889 * @this {WebInspector.ObjectPropertyTreeElement} |
| 890 */ |
| 891 function callback(error) { |
| 892 if (error) { |
| 893 this.update(); |
| 894 return; |
| 895 } |
| 896 |
| 897 if (!expression) { |
| 898 // The property was deleted, so remove this tree element. |
| 899 this.parent.removeChild(this); |
| 900 } else { |
| 901 // Call updateSiblings since their value might be based on the value tha
t just changed. |
| 902 var parent = this.parent; |
| 903 parent.invalidateChildren(); |
| 904 parent.onpopulate(); |
| 905 } |
| 906 } |
| 907 } |
| 908 |
| 909 /** |
| 910 * @param {?WebInspector.RemoteObject} result |
| 911 * @param {boolean=} wasThrown |
| 912 */ |
| 913 _onInvokeGetterClick(result, wasThrown) { |
| 914 if (!result) |
| 915 return; |
| 916 this.property.value = result; |
| 917 this.property.wasThrown = wasThrown; |
| 918 |
| 919 this.update(); |
| 920 this.invalidateChildren(); |
| 921 this._updateExpandable(); |
| 922 } |
| 923 |
| 924 _updateExpandable() { |
| 925 if (this.property.value) |
| 926 this.setExpandable( |
| 927 !this.property.value.customPreview() && this.property.value.hasChildre
n && !this.property.wasThrown); |
| 928 else |
| 929 this.setExpandable(false); |
| 930 } |
696 }; | 931 }; |
697 | 932 |
| 933 |
698 /** | 934 /** |
699 * @param {?WebInspector.RemoteObject} object | 935 * @unrestricted |
700 * @param {!Array.<string>} propertyPath | |
701 * @param {function(?WebInspector.RemoteObject, boolean=)} callback | |
702 * @return {!Element} | |
703 */ | 936 */ |
704 WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan =
function(object, propertyPath, callback) | 937 WebInspector.ArrayGroupingTreeElement = class extends TreeElement { |
705 { | 938 /** |
706 var rootElement = createElement("span"); | 939 * @param {!WebInspector.RemoteObject} object |
707 var element = rootElement.createChild("span"); | 940 * @param {number} fromIndex |
708 element.textContent = WebInspector.UIString("(...)"); | 941 * @param {number} toIndex |
709 if (!object) | 942 * @param {number} propertyCount |
710 return rootElement; | 943 * @param {!WebInspector.Linkifier=} linkifier |
711 element.classList.add("object-value-calculate-value-button"); | 944 */ |
712 element.title = WebInspector.UIString("Invoke property getter"); | 945 constructor(object, fromIndex, toIndex, propertyCount, linkifier) { |
713 element.addEventListener("click", onInvokeGetterClick, false); | 946 super(String.sprintf('[%d \u2026 %d]', fromIndex, toIndex), true); |
714 | |
715 function onInvokeGetterClick(event) | |
716 { | |
717 event.consume(); | |
718 object.getProperty(propertyPath, callback); | |
719 } | |
720 | |
721 return rootElement; | |
722 }; | |
723 | |
724 /** | |
725 * @constructor | |
726 * @extends {TreeElement} | |
727 * @param {!WebInspector.RemoteObject} object | |
728 * @param {number} fromIndex | |
729 * @param {number} toIndex | |
730 * @param {number} propertyCount | |
731 * @param {!WebInspector.Linkifier=} linkifier | |
732 */ | |
733 WebInspector.ArrayGroupingTreeElement = function(object, fromIndex, toIndex, pro
pertyCount, linkifier) | |
734 { | |
735 TreeElement.call(this, String.sprintf("[%d \u2026 %d]", fromIndex, toIndex),
true); | |
736 this.toggleOnClick = true; | 947 this.toggleOnClick = true; |
737 this.selectable = false; | 948 this.selectable = false; |
738 this._fromIndex = fromIndex; | 949 this._fromIndex = fromIndex; |
739 this._toIndex = toIndex; | 950 this._toIndex = toIndex; |
740 this._object = object; | 951 this._object = object; |
741 this._readOnly = true; | 952 this._readOnly = true; |
742 this._propertyCount = propertyCount; | 953 this._propertyCount = propertyCount; |
743 this._linkifier = linkifier; | 954 this._linkifier = linkifier; |
744 }; | 955 } |
745 | 956 |
746 WebInspector.ArrayGroupingTreeElement._bucketThreshold = 100; | 957 /** |
747 WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold = 250000; | 958 * @param {!TreeElement} treeNode |
748 WebInspector.ArrayGroupingTreeElement._getOwnPropertyNamesThreshold = 500000; | 959 * @param {!WebInspector.RemoteObject} object |
| 960 * @param {number} fromIndex |
| 961 * @param {number} toIndex |
| 962 * @param {!WebInspector.Linkifier=} linkifier |
| 963 */ |
| 964 static _populateArray(treeNode, object, fromIndex, toIndex, linkifier) { |
| 965 WebInspector.ArrayGroupingTreeElement._populateRanges(treeNode, object, from
Index, toIndex, true, linkifier); |
| 966 } |
749 | 967 |
750 /** | 968 /** |
751 * @param {!TreeElement} treeNode | 969 * @param {!TreeElement} treeNode |
752 * @param {!WebInspector.RemoteObject} object | 970 * @param {!WebInspector.RemoteObject} object |
753 * @param {number} fromIndex | 971 * @param {number} fromIndex |
754 * @param {number} toIndex | 972 * @param {number} toIndex |
755 * @param {!WebInspector.Linkifier=} linkifier | 973 * @param {boolean} topLevel |
756 */ | 974 * @param {!WebInspector.Linkifier=} linkifier |
757 WebInspector.ArrayGroupingTreeElement._populateArray = function(treeNode, object
, fromIndex, toIndex, linkifier) | 975 * @this {WebInspector.ArrayGroupingTreeElement} |
758 { | 976 */ |
759 WebInspector.ArrayGroupingTreeElement._populateRanges(treeNode, object, from
Index, toIndex, true, linkifier); | 977 static _populateRanges(treeNode, object, fromIndex, toIndex, topLevel, linkifi
er) { |
760 }; | 978 object.callFunctionJSON( |
761 | 979 packRanges, |
762 /** | 980 [ |
763 * @param {!TreeElement} treeNode | 981 {value: fromIndex}, {value: toIndex}, {value: WebInspector.ArrayGroupi
ngTreeElement._bucketThreshold}, |
764 * @param {!WebInspector.RemoteObject} object | 982 {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshol
d}, |
765 * @param {number} fromIndex | 983 {value: WebInspector.ArrayGroupingTreeElement._getOwnPropertyNamesThre
shold} |
766 * @param {number} toIndex | 984 ], |
767 * @param {boolean} topLevel | 985 callback); |
768 * @param {!WebInspector.Linkifier=} linkifier | |
769 * @this {WebInspector.ArrayGroupingTreeElement} | |
770 */ | |
771 WebInspector.ArrayGroupingTreeElement._populateRanges = function(treeNode, objec
t, fromIndex, toIndex, topLevel, linkifier) | |
772 { | |
773 object.callFunctionJSON(packRanges, [ | |
774 { value: fromIndex }, | |
775 { value: toIndex }, | |
776 { value: WebInspector.ArrayGroupingTreeElement._bucketThreshold }, | |
777 { value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold
}, | |
778 { value: WebInspector.ArrayGroupingTreeElement._getOwnPropertyNamesThres
hold } | |
779 ], callback); | |
780 | 986 |
781 /** | 987 /** |
782 * Note: must declare params as optional. | 988 * Note: must declare params as optional. |
783 * @param {number=} fromIndex | 989 * @param {number=} fromIndex |
784 * @param {number=} toIndex | 990 * @param {number=} toIndex |
785 * @param {number=} bucketThreshold | 991 * @param {number=} bucketThreshold |
786 * @param {number=} sparseIterationThreshold | 992 * @param {number=} sparseIterationThreshold |
787 * @param {number=} getOwnPropertyNamesThreshold | 993 * @param {number=} getOwnPropertyNamesThreshold |
788 * @suppressReceiverCheck | 994 * @suppressReceiverCheck |
789 * @this {Object} | 995 * @this {Object} |
790 */ | 996 */ |
791 function packRanges(fromIndex, toIndex, bucketThreshold, sparseIterationThre
shold, getOwnPropertyNamesThreshold) | 997 function packRanges(fromIndex, toIndex, bucketThreshold, sparseIterationThre
shold, getOwnPropertyNamesThreshold) { |
792 { | 998 var ownPropertyNames = null; |
793 var ownPropertyNames = null; | 999 var consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) &
& ArrayBuffer.isView(this); |
794 var consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold)
&& ArrayBuffer.isView(this); | 1000 var skipGetOwnPropertyNames = consecutiveRange && (toIndex - fromIndex >=
getOwnPropertyNamesThreshold); |
795 var skipGetOwnPropertyNames = consecutiveRange && (toIndex - fromIndex >
= getOwnPropertyNamesThreshold); | |
796 | 1001 |
797 function* arrayIndexes(object) | 1002 function* arrayIndexes(object) { |
798 { | 1003 if (toIndex - fromIndex < sparseIterationThreshold) { |
799 if (toIndex - fromIndex < sparseIterationThreshold) { | 1004 for (var i = fromIndex; i <= toIndex; ++i) { |
800 for (var i = fromIndex; i <= toIndex; ++i) { | 1005 if (i in object) |
801 if (i in object) | 1006 yield i; |
802 yield i; | 1007 } |
803 } | 1008 } else { |
804 } else { | 1009 ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(obje
ct); |
805 ownPropertyNames = ownPropertyNames || Object.getOwnPropertyName
s(object); | 1010 for (var i = 0; i < ownPropertyNames.length; ++i) { |
806 for (var i = 0; i < ownPropertyNames.length; ++i) { | 1011 var name = ownPropertyNames[i]; |
807 var name = ownPropertyNames[i]; | 1012 var index = name >>> 0; |
808 var index = name >>> 0; | 1013 if (('' + index) === name && fromIndex <= index && index <= toIndex) |
809 if (("" + index) === name && fromIndex <= index && index <=
toIndex) | 1014 yield index; |
810 yield index; | 1015 } |
811 } | |
812 } | |
813 } | 1016 } |
| 1017 } |
814 | 1018 |
815 var count = 0; | 1019 var count = 0; |
816 if (consecutiveRange) { | 1020 if (consecutiveRange) { |
817 count = toIndex - fromIndex + 1; | 1021 count = toIndex - fromIndex + 1; |
818 } else { | 1022 } else { |
819 for (var i of arrayIndexes(this)) | 1023 for (var i of arrayIndexes(this)) |
820 ++count; | 1024 ++count; |
| 1025 } |
| 1026 |
| 1027 var bucketSize = count; |
| 1028 if (count <= bucketThreshold) |
| 1029 bucketSize = count; |
| 1030 else |
| 1031 bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.
log(bucketThreshold)) - 1); |
| 1032 |
| 1033 var ranges = []; |
| 1034 if (consecutiveRange) { |
| 1035 for (var i = fromIndex; i <= toIndex; i += bucketSize) { |
| 1036 var groupStart = i; |
| 1037 var groupEnd = groupStart + bucketSize - 1; |
| 1038 if (groupEnd > toIndex) |
| 1039 groupEnd = toIndex; |
| 1040 ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]); |
821 } | 1041 } |
| 1042 } else { |
| 1043 count = 0; |
| 1044 var groupStart = -1; |
| 1045 var groupEnd = 0; |
| 1046 for (var i of arrayIndexes(this)) { |
| 1047 if (groupStart === -1) |
| 1048 groupStart = i; |
| 1049 groupEnd = i; |
| 1050 if (++count === bucketSize) { |
| 1051 ranges.push([groupStart, groupEnd, count]); |
| 1052 count = 0; |
| 1053 groupStart = -1; |
| 1054 } |
| 1055 } |
| 1056 if (count > 0) |
| 1057 ranges.push([groupStart, groupEnd, count]); |
| 1058 } |
822 | 1059 |
823 var bucketSize = count; | 1060 return {ranges: ranges, skipGetOwnPropertyNames: skipGetOwnPropertyNames}; |
824 if (count <= bucketThreshold) | |
825 bucketSize = count; | |
826 else | |
827 bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / M
ath.log(bucketThreshold)) - 1); | |
828 | |
829 var ranges = []; | |
830 if (consecutiveRange) { | |
831 for (var i = fromIndex; i <= toIndex; i += bucketSize) { | |
832 var groupStart = i; | |
833 var groupEnd = groupStart + bucketSize - 1; | |
834 if (groupEnd > toIndex) | |
835 groupEnd = toIndex; | |
836 ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]); | |
837 } | |
838 } else { | |
839 count = 0; | |
840 var groupStart = -1; | |
841 var groupEnd = 0; | |
842 for (var i of arrayIndexes(this)) { | |
843 if (groupStart === -1) | |
844 groupStart = i; | |
845 groupEnd = i; | |
846 if (++count === bucketSize) { | |
847 ranges.push([groupStart, groupEnd, count]); | |
848 count = 0; | |
849 groupStart = -1; | |
850 } | |
851 } | |
852 if (count > 0) | |
853 ranges.push([groupStart, groupEnd, count]); | |
854 } | |
855 | |
856 return { ranges: ranges, skipGetOwnPropertyNames: skipGetOwnPropertyName
s }; | |
857 } | 1061 } |
858 | 1062 |
859 function callback(result) | 1063 function callback(result) { |
860 { | 1064 if (!result) |
861 if (!result) | 1065 return; |
862 return; | 1066 var ranges = /** @type {!Array.<!Array.<number>>} */ (result.ranges); |
863 var ranges = /** @type {!Array.<!Array.<number>>} */ (result.ranges); | 1067 if (ranges.length === 1) { |
864 if (ranges.length === 1) { | 1068 WebInspector.ArrayGroupingTreeElement._populateAsFragment( |
865 WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeNode,
object, ranges[0][0], ranges[0][1], linkifier); | 1069 treeNode, object, ranges[0][0], ranges[0][1], linkifier); |
866 } else { | 1070 } else { |
867 for (var i = 0; i < ranges.length; ++i) { | 1071 for (var i = 0; i < ranges.length; ++i) { |
868 var fromIndex = ranges[i][0]; | 1072 var fromIndex = ranges[i][0]; |
869 var toIndex = ranges[i][1]; | 1073 var toIndex = ranges[i][1]; |
870 var count = ranges[i][2]; | 1074 var count = ranges[i][2]; |
871 if (fromIndex === toIndex) | 1075 if (fromIndex === toIndex) |
872 WebInspector.ArrayGroupingTreeElement._populateAsFragment(tr
eeNode, object, fromIndex, toIndex, linkifier); | 1076 WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeNode,
object, fromIndex, toIndex, linkifier); |
873 else | 1077 else |
874 treeNode.appendChild(new WebInspector.ArrayGroupingTreeEleme
nt(object, fromIndex, toIndex, count, linkifier)); | 1078 treeNode.appendChild( |
875 } | 1079 new WebInspector.ArrayGroupingTreeElement(object, fromIndex, toI
ndex, count, linkifier)); |
876 } | 1080 } |
877 if (topLevel) | 1081 } |
878 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties(tr
eeNode, object, result.skipGetOwnPropertyNames, linkifier); | 1082 if (topLevel) |
| 1083 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties( |
| 1084 treeNode, object, result.skipGetOwnPropertyNames, linkifier); |
879 } | 1085 } |
880 }; | 1086 } |
881 | 1087 |
882 /** | 1088 /** |
883 * @param {!TreeElement} treeNode | 1089 * @param {!TreeElement} treeNode |
884 * @param {!WebInspector.RemoteObject} object | 1090 * @param {!WebInspector.RemoteObject} object |
885 * @param {number} fromIndex | 1091 * @param {number} fromIndex |
886 * @param {number} toIndex | 1092 * @param {number} toIndex |
887 * @param {!WebInspector.Linkifier=} linkifier | 1093 * @param {!WebInspector.Linkifier=} linkifier |
888 * @this {WebInspector.ArrayGroupingTreeElement} | 1094 * @this {WebInspector.ArrayGroupingTreeElement} |
889 */ | 1095 */ |
890 WebInspector.ArrayGroupingTreeElement._populateAsFragment = function(treeNode, o
bject, fromIndex, toIndex, linkifier) | 1096 static _populateAsFragment(treeNode, object, fromIndex, toIndex, linkifier) { |
891 { | 1097 object.callFunction( |
892 object.callFunction(buildArrayFragment, [{value: fromIndex}, {value: toIndex
}, {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold}], pr
ocessArrayFragment.bind(this)); | 1098 buildArrayFragment, |
| 1099 [ |
| 1100 {value: fromIndex}, {value: toIndex}, |
| 1101 {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshol
d} |
| 1102 ], |
| 1103 processArrayFragment.bind(this)); |
893 | 1104 |
894 /** | 1105 /** |
895 * @suppressReceiverCheck | 1106 * @suppressReceiverCheck |
896 * @this {Object} | 1107 * @this {Object} |
897 * @param {number=} fromIndex // must declare optional | 1108 * @param {number=} fromIndex // must declare optional |
898 * @param {number=} toIndex // must declare optional | 1109 * @param {number=} toIndex // must declare optional |
899 * @param {number=} sparseIterationThreshold // must declare optional | 1110 * @param {number=} sparseIterationThreshold // must declare optional |
900 */ | 1111 */ |
901 function buildArrayFragment(fromIndex, toIndex, sparseIterationThreshold) | 1112 function buildArrayFragment(fromIndex, toIndex, sparseIterationThreshold) { |
902 { | 1113 var result = Object.create(null); |
903 var result = Object.create(null); | 1114 if (toIndex - fromIndex < sparseIterationThreshold) { |
904 if (toIndex - fromIndex < sparseIterationThreshold) { | 1115 for (var i = fromIndex; i <= toIndex; ++i) { |
905 for (var i = fromIndex; i <= toIndex; ++i) { | 1116 if (i in this) |
906 if (i in this) | 1117 result[i] = this[i]; |
907 result[i] = this[i]; | |
908 } | |
909 } else { | |
910 var ownPropertyNames = Object.getOwnPropertyNames(this); | |
911 for (var i = 0; i < ownPropertyNames.length; ++i) { | |
912 var name = ownPropertyNames[i]; | |
913 var index = name >>> 0; | |
914 if (String(index) === name && fromIndex <= index && index <= toI
ndex) | |
915 result[index] = this[index]; | |
916 } | |
917 } | 1118 } |
918 return result; | 1119 } else { |
| 1120 var ownPropertyNames = Object.getOwnPropertyNames(this); |
| 1121 for (var i = 0; i < ownPropertyNames.length; ++i) { |
| 1122 var name = ownPropertyNames[i]; |
| 1123 var index = name >>> 0; |
| 1124 if (String(index) === name && fromIndex <= index && index <= toIndex) |
| 1125 result[index] = this[index]; |
| 1126 } |
| 1127 } |
| 1128 return result; |
919 } | 1129 } |
920 | 1130 |
921 /** | 1131 /** |
922 * @param {?WebInspector.RemoteObject} arrayFragment | 1132 * @param {?WebInspector.RemoteObject} arrayFragment |
923 * @param {boolean=} wasThrown | 1133 * @param {boolean=} wasThrown |
924 * @this {WebInspector.ArrayGroupingTreeElement} | 1134 * @this {WebInspector.ArrayGroupingTreeElement} |
925 */ | 1135 */ |
926 function processArrayFragment(arrayFragment, wasThrown) | 1136 function processArrayFragment(arrayFragment, wasThrown) { |
927 { | 1137 if (!arrayFragment || wasThrown) |
928 if (!arrayFragment || wasThrown) | 1138 return; |
929 return; | 1139 arrayFragment.getAllProperties(false, processProperties.bind(this)); |
930 arrayFragment.getAllProperties(false, processProperties.bind(this)); | |
931 } | 1140 } |
932 | 1141 |
933 /** @this {WebInspector.ArrayGroupingTreeElement} */ | 1142 /** @this {WebInspector.ArrayGroupingTreeElement} */ |
934 function processProperties(properties, internalProperties) | 1143 function processProperties(properties, internalProperties) { |
935 { | 1144 if (!properties) |
936 if (!properties) | 1145 return; |
937 return; | |
938 | 1146 |
939 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); | 1147 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); |
940 for (var i = 0; i < properties.length; ++i) { | 1148 for (var i = 0; i < properties.length; ++i) { |
941 properties[i].parentObject = this._object; | 1149 properties[i].parentObject = this._object; |
942 var childTreeElement = new WebInspector.ObjectPropertyTreeElement(pr
operties[i], linkifier); | 1150 var childTreeElement = new WebInspector.ObjectPropertyTreeElement(proper
ties[i], linkifier); |
943 childTreeElement._readOnly = true; | 1151 childTreeElement._readOnly = true; |
944 treeNode.appendChild(childTreeElement); | 1152 treeNode.appendChild(childTreeElement); |
945 } | 1153 } |
946 } | 1154 } |
947 }; | 1155 } |
948 | 1156 |
949 /** | 1157 /** |
950 * @param {!TreeElement} treeNode | 1158 * @param {!TreeElement} treeNode |
951 * @param {!WebInspector.RemoteObject} object | 1159 * @param {!WebInspector.RemoteObject} object |
952 * @param {boolean} skipGetOwnPropertyNames | 1160 * @param {boolean} skipGetOwnPropertyNames |
953 * @param {!WebInspector.Linkifier=} linkifier | 1161 * @param {!WebInspector.Linkifier=} linkifier |
954 * @this {WebInspector.ArrayGroupingTreeElement} | 1162 * @this {WebInspector.ArrayGroupingTreeElement} |
955 */ | 1163 */ |
956 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties = function(tre
eNode, object, skipGetOwnPropertyNames, linkifier) | 1164 static _populateNonIndexProperties(treeNode, object, skipGetOwnPropertyNames,
linkifier) { |
957 { | |
958 object.callFunction(buildObjectFragment, [{value: skipGetOwnPropertyNames}],
processObjectFragment.bind(this)); | 1165 object.callFunction(buildObjectFragment, [{value: skipGetOwnPropertyNames}],
processObjectFragment.bind(this)); |
959 | 1166 |
960 /** | 1167 /** |
961 * @param {boolean=} skipGetOwnPropertyNames | 1168 * @param {boolean=} skipGetOwnPropertyNames |
962 * @suppressReceiverCheck | 1169 * @suppressReceiverCheck |
963 * @this {Object} | 1170 * @this {Object} |
964 */ | 1171 */ |
965 function buildObjectFragment(skipGetOwnPropertyNames) | 1172 function buildObjectFragment(skipGetOwnPropertyNames) { |
966 { | 1173 var result = {__proto__: this.__proto__}; |
967 var result = { __proto__: this.__proto__ }; | 1174 if (skipGetOwnPropertyNames) |
968 if (skipGetOwnPropertyNames) | |
969 return result; | |
970 var names = Object.getOwnPropertyNames(this); | |
971 for (var i = 0; i < names.length; ++i) { | |
972 var name = names[i]; | |
973 // Array index check according to the ES5-15.4. | |
974 if (String(name >>> 0) === name && name >>> 0 !== 0xffffffff) | |
975 continue; | |
976 var descriptor = Object.getOwnPropertyDescriptor(this, name); | |
977 if (descriptor) | |
978 Object.defineProperty(result, name, descriptor); | |
979 } | |
980 return result; | 1175 return result; |
| 1176 var names = Object.getOwnPropertyNames(this); |
| 1177 for (var i = 0; i < names.length; ++i) { |
| 1178 var name = names[i]; |
| 1179 // Array index check according to the ES5-15.4. |
| 1180 if (String(name >>> 0) === name && name >>> 0 !== 0xffffffff) |
| 1181 continue; |
| 1182 var descriptor = Object.getOwnPropertyDescriptor(this, name); |
| 1183 if (descriptor) |
| 1184 Object.defineProperty(result, name, descriptor); |
| 1185 } |
| 1186 return result; |
981 } | 1187 } |
982 | 1188 |
983 /** | 1189 /** |
984 * @param {?WebInspector.RemoteObject} arrayFragment | 1190 * @param {?WebInspector.RemoteObject} arrayFragment |
985 * @param {boolean=} wasThrown | 1191 * @param {boolean=} wasThrown |
986 * @this {WebInspector.ArrayGroupingTreeElement} | 1192 * @this {WebInspector.ArrayGroupingTreeElement} |
987 */ | 1193 */ |
988 function processObjectFragment(arrayFragment, wasThrown) | 1194 function processObjectFragment(arrayFragment, wasThrown) { |
989 { | 1195 if (!arrayFragment || wasThrown) |
990 if (!arrayFragment || wasThrown) | 1196 return; |
991 return; | 1197 arrayFragment.getOwnProperties(processProperties.bind(this)); |
992 arrayFragment.getOwnProperties(processProperties.bind(this)); | |
993 } | 1198 } |
994 | 1199 |
995 /** | 1200 /** |
996 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties | 1201 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties |
997 * @param {?Array.<!WebInspector.RemoteObjectProperty>=} internalProperties | 1202 * @param {?Array.<!WebInspector.RemoteObjectProperty>=} internalProperties |
998 * @this {WebInspector.ArrayGroupingTreeElement} | 1203 * @this {WebInspector.ArrayGroupingTreeElement} |
999 */ | 1204 */ |
1000 function processProperties(properties, internalProperties) | 1205 function processProperties(properties, internalProperties) { |
1001 { | 1206 if (!properties) |
1002 if (!properties) | 1207 return; |
1003 return; | 1208 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); |
1004 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); | 1209 for (var i = 0; i < properties.length; ++i) { |
1005 for (var i = 0; i < properties.length; ++i) { | 1210 properties[i].parentObject = this._object; |
1006 properties[i].parentObject = this._object; | 1211 var childTreeElement = new WebInspector.ObjectPropertyTreeElement(proper
ties[i], linkifier); |
1007 var childTreeElement = new WebInspector.ObjectPropertyTreeElement(pr
operties[i], linkifier); | 1212 childTreeElement._readOnly = true; |
1008 childTreeElement._readOnly = true; | 1213 treeNode.appendChild(childTreeElement); |
1009 treeNode.appendChild(childTreeElement); | 1214 } |
1010 } | |
1011 } | 1215 } |
| 1216 } |
| 1217 |
| 1218 /** |
| 1219 * @override |
| 1220 */ |
| 1221 onpopulate() { |
| 1222 if (this._propertyCount >= WebInspector.ArrayGroupingTreeElement._bucketThre
shold) { |
| 1223 WebInspector.ArrayGroupingTreeElement._populateRanges( |
| 1224 this, this._object, this._fromIndex, this._toIndex, false, this._linki
fier); |
| 1225 return; |
| 1226 } |
| 1227 WebInspector.ArrayGroupingTreeElement._populateAsFragment( |
| 1228 this, this._object, this._fromIndex, this._toIndex, this._linkifier); |
| 1229 } |
| 1230 |
| 1231 /** |
| 1232 * @override |
| 1233 */ |
| 1234 onattach() { |
| 1235 this.listItemElement.classList.add('object-properties-section-name'); |
| 1236 } |
1012 }; | 1237 }; |
1013 | 1238 |
1014 WebInspector.ArrayGroupingTreeElement.prototype = { | 1239 WebInspector.ArrayGroupingTreeElement._bucketThreshold = 100; |
1015 onpopulate: function() | 1240 WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold = 250000; |
1016 { | 1241 WebInspector.ArrayGroupingTreeElement._getOwnPropertyNamesThreshold = 500000; |
1017 if (this._propertyCount >= WebInspector.ArrayGroupingTreeElement._bucket
Threshold) { | |
1018 WebInspector.ArrayGroupingTreeElement._populateRanges(this, this._ob
ject, this._fromIndex, this._toIndex, false, this._linkifier); | |
1019 return; | |
1020 } | |
1021 WebInspector.ArrayGroupingTreeElement._populateAsFragment(this, this._ob
ject, this._fromIndex, this._toIndex, this._linkifier); | |
1022 }, | |
1023 | 1242 |
1024 onattach: function() | |
1025 { | |
1026 this.listItemElement.classList.add("object-properties-section-name"); | |
1027 }, | |
1028 | 1243 |
1029 __proto__: TreeElement.prototype | 1244 /** |
| 1245 * @unrestricted |
| 1246 */ |
| 1247 WebInspector.ObjectPropertyPrompt = class extends WebInspector.TextPrompt { |
| 1248 constructor() { |
| 1249 super(); |
| 1250 this.initialize(WebInspector.ExecutionContextSelector.completionsForTextProm
ptInCurrentContext); |
| 1251 this.setSuggestBoxEnabled(true); |
| 1252 } |
1030 }; | 1253 }; |
1031 | 1254 |
1032 /** | |
1033 * @constructor | |
1034 * @extends {WebInspector.TextPrompt} | |
1035 */ | |
1036 WebInspector.ObjectPropertyPrompt = function() | |
1037 { | |
1038 WebInspector.TextPrompt.call(this); | |
1039 this.initialize(WebInspector.ExecutionContextSelector.completionsForTextProm
ptInCurrentContext); | |
1040 this.setSuggestBoxEnabled(true); | |
1041 }; | |
1042 | |
1043 WebInspector.ObjectPropertyPrompt.prototype = { | |
1044 __proto__: WebInspector.TextPrompt.prototype | |
1045 }; | |
1046 | |
1047 /** | |
1048 * @param {?string} name | |
1049 * @return {!Element} | |
1050 */ | |
1051 WebInspector.ObjectPropertiesSection.createNameElement = function(name) | |
1052 { | |
1053 var nameElement = createElementWithClass("span", "name"); | |
1054 if (/^\s|\s$|^$|\n/.test(name)) | |
1055 nameElement.createTextChildren("\"", name.replace(/\n/g, "\u21B5"), "\""
); | |
1056 else | |
1057 nameElement.textContent = name; | |
1058 return nameElement; | |
1059 }; | |
1060 | 1255 |
1061 WebInspector.ObjectPropertiesSection._functionPrefixSource = /^(?:async\s)?funct
ion\*?\s/; | 1256 WebInspector.ObjectPropertiesSection._functionPrefixSource = /^(?:async\s)?funct
ion\*?\s/; |
1062 | 1257 |
| 1258 |
1063 /** | 1259 /** |
1064 * @param {?string=} description | 1260 * @unrestricted |
1065 * @return {string} valueText | |
1066 */ | 1261 */ |
1067 WebInspector.ObjectPropertiesSection.valueTextForFunctionDescription = function(
description) | 1262 WebInspector.ObjectPropertiesSectionExpandController = class { |
1068 { | 1263 constructor() { |
1069 var text = description.replace(/^function [gs]et /, "function "); | 1264 /** @type {!Set.<string>} */ |
1070 var functionPrefixWithArguments = new RegExp(WebInspector.ObjectPropertiesSe
ction._functionPrefixSource.source + "([^)]*)"); | 1265 this._expandedProperties = new Set(); |
1071 var matches = functionPrefixWithArguments.exec(text); | 1266 } |
1072 if (!matches) { | 1267 |
1073 // process shorthand methods | 1268 /** |
1074 matches = /[^(]*(\([^)]*)/.exec(text); | 1269 * @param {string} id |
| 1270 * @param {!WebInspector.ObjectPropertiesSection} section |
| 1271 */ |
| 1272 watchSection(id, section) { |
| 1273 section.addEventListener(TreeOutline.Events.ElementAttached, this._elementAt
tached, this); |
| 1274 section.addEventListener(TreeOutline.Events.ElementExpanded, this._elementEx
panded, this); |
| 1275 section.addEventListener(TreeOutline.Events.ElementCollapsed, this._elementC
ollapsed, this); |
| 1276 section[WebInspector.ObjectPropertiesSectionExpandController._treeOutlineId]
= id; |
| 1277 |
| 1278 if (this._expandedProperties.has(id)) |
| 1279 section.expand(); |
| 1280 } |
| 1281 |
| 1282 /** |
| 1283 * @param {string} id |
| 1284 */ |
| 1285 stopWatchSectionsWithId(id) { |
| 1286 for (var property of this._expandedProperties) { |
| 1287 if (property.startsWith(id + ':')) |
| 1288 this._expandedProperties.delete(property); |
1075 } | 1289 } |
1076 var match = matches ? matches[1] : null; | 1290 } |
1077 return match ? match.replace(/\n/g, " ") + ")" : (text || ""); | 1291 |
| 1292 /** |
| 1293 * @param {!WebInspector.Event} event |
| 1294 */ |
| 1295 _elementAttached(event) { |
| 1296 var element = /** @type {!TreeElement} */ (event.data); |
| 1297 if (element.isExpandable() && this._expandedProperties.has(this._propertyPat
h(element))) |
| 1298 element.expand(); |
| 1299 } |
| 1300 |
| 1301 /** |
| 1302 * @param {!WebInspector.Event} event |
| 1303 */ |
| 1304 _elementExpanded(event) { |
| 1305 var element = /** @type {!TreeElement} */ (event.data); |
| 1306 this._expandedProperties.add(this._propertyPath(element)); |
| 1307 } |
| 1308 |
| 1309 /** |
| 1310 * @param {!WebInspector.Event} event |
| 1311 */ |
| 1312 _elementCollapsed(event) { |
| 1313 var element = /** @type {!TreeElement} */ (event.data); |
| 1314 this._expandedProperties.delete(this._propertyPath(element)); |
| 1315 } |
| 1316 |
| 1317 /** |
| 1318 * @param {!TreeElement} treeElement |
| 1319 * @return {string} |
| 1320 */ |
| 1321 _propertyPath(treeElement) { |
| 1322 var cachedPropertyPath = treeElement[WebInspector.ObjectPropertiesSectionExp
andController._cachedPathSymbol]; |
| 1323 if (cachedPropertyPath) |
| 1324 return cachedPropertyPath; |
| 1325 |
| 1326 var current = treeElement; |
| 1327 var rootElement = treeElement.treeOutline.objectTreeElement(); |
| 1328 |
| 1329 var result; |
| 1330 |
| 1331 while (current !== rootElement) { |
| 1332 var currentName = ''; |
| 1333 if (current.property) |
| 1334 currentName = current.property.name; |
| 1335 else |
| 1336 currentName = typeof current.title === 'string' ? current.title : curren
t.title.textContent; |
| 1337 |
| 1338 result = currentName + (result ? '.' + result : ''); |
| 1339 current = current.parent; |
| 1340 } |
| 1341 var treeOutlineId = treeElement.treeOutline[WebInspector.ObjectPropertiesSec
tionExpandController._treeOutlineId]; |
| 1342 result = treeOutlineId + (result ? ':' + result : ''); |
| 1343 treeElement[WebInspector.ObjectPropertiesSectionExpandController._cachedPath
Symbol] = result; |
| 1344 return result; |
| 1345 } |
1078 }; | 1346 }; |
1079 | 1347 |
1080 /** | 1348 WebInspector.ObjectPropertiesSectionExpandController._cachedPathSymbol = Symbol(
'cachedPath'); |
1081 * @param {!WebInspector.RemoteObject} value | 1349 WebInspector.ObjectPropertiesSectionExpandController._treeOutlineId = Symbol('tr
eeOutlineId'); |
1082 * @param {boolean} wasThrown | |
1083 * @param {!Element=} parentElement | |
1084 * @param {!WebInspector.Linkifier=} linkifier | |
1085 * @return {!Element} | |
1086 */ | |
1087 WebInspector.ObjectPropertiesSection.createValueElementWithCustomSupport = funct
ion(value, wasThrown, parentElement, linkifier) | |
1088 { | |
1089 if (value.customPreview()) { | |
1090 var result = (new WebInspector.CustomPreviewComponent(value)).element; | |
1091 result.classList.add("object-properties-section-custom-section"); | |
1092 return result; | |
1093 } | |
1094 return WebInspector.ObjectPropertiesSection.createValueElement(value, wasThr
own, parentElement, linkifier); | |
1095 }; | |
1096 | |
1097 /** | |
1098 * @param {!WebInspector.RemoteObject} value | |
1099 * @param {boolean} wasThrown | |
1100 * @param {!Element=} parentElement | |
1101 * @param {!WebInspector.Linkifier=} linkifier | |
1102 * @return {!Element} | |
1103 */ | |
1104 WebInspector.ObjectPropertiesSection.createValueElement = function(value, wasThr
own, parentElement, linkifier) | |
1105 { | |
1106 var valueElement = createElementWithClass("span", "value"); | |
1107 var type = value.type; | |
1108 var subtype = value.subtype; | |
1109 var description = value.description; | |
1110 var prefix; | |
1111 var valueText; | |
1112 var suffix; | |
1113 if (wasThrown) { | |
1114 prefix = "[Exception: "; | |
1115 valueText = description; | |
1116 suffix = "]"; | |
1117 } else if (type === "string" && typeof description === "string") { | |
1118 // Render \n as a nice unicode cr symbol. | |
1119 prefix = "\""; | |
1120 valueText = description.replace(/\n/g, "\u21B5"); | |
1121 suffix = "\""; | |
1122 } else if (type === "function") { | |
1123 valueText = WebInspector.ObjectPropertiesSection.valueTextForFunctionDes
cription(description); | |
1124 } else if (type !== "object" || subtype !== "node") { | |
1125 valueText = description; | |
1126 } | |
1127 if (type !== "number" || valueText.indexOf("e") === -1) { | |
1128 valueElement.setTextContentTruncatedIfNeeded(valueText || ""); | |
1129 if (prefix) | |
1130 valueElement.insertBefore(createTextNode(prefix), valueElement.first
Child); | |
1131 if (suffix) | |
1132 valueElement.createTextChild(suffix); | |
1133 } else { | |
1134 var numberParts = valueText.split("e"); | |
1135 var mantissa = valueElement.createChild("span", "object-value-scientific
-notation-mantissa"); | |
1136 mantissa.textContent = numberParts[0]; | |
1137 var exponent = valueElement.createChild("span", "object-value-scientific
-notation-exponent"); | |
1138 exponent.textContent = "e" + numberParts[1]; | |
1139 valueElement.classList.add("object-value-scientific-notation-number"); | |
1140 if (parentElement) // FIXME: do it in the caller. | |
1141 parentElement.classList.add("hbox"); | |
1142 } | |
1143 | |
1144 if (wasThrown) | |
1145 valueElement.classList.add("error"); | |
1146 if (subtype || type) | |
1147 valueElement.classList.add("object-value-" + (subtype || type)); | |
1148 | |
1149 if (type === "object" && subtype === "node" && description) { | |
1150 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(valueElement,
description); | |
1151 valueElement.addEventListener("click", mouseClick, false); | |
1152 valueElement.addEventListener("mousemove", mouseMove, false); | |
1153 valueElement.addEventListener("mouseleave", mouseLeave, false); | |
1154 } else { | |
1155 valueElement.title = description || ""; | |
1156 } | |
1157 | |
1158 if (type === "object" && subtype === "internal#location") { | |
1159 var rawLocation = value.debuggerModel().createRawLocationByScriptId(valu
e.value.scriptId, value.value.lineNumber, value.value.columnNumber); | |
1160 if (rawLocation && linkifier) | |
1161 return linkifier.linkifyRawLocation(rawLocation, ""); | |
1162 valueElement.textContent = "<unknown>"; | |
1163 } | |
1164 | |
1165 function mouseMove() | |
1166 { | |
1167 WebInspector.DOMModel.highlightObjectAsDOMNode(value); | |
1168 } | |
1169 | |
1170 function mouseLeave() | |
1171 { | |
1172 WebInspector.DOMModel.hideDOMNodeHighlight(); | |
1173 } | |
1174 | |
1175 /** | |
1176 * @param {!Event} event | |
1177 */ | |
1178 function mouseClick(event) | |
1179 { | |
1180 WebInspector.Revealer.reveal(value); | |
1181 event.consume(true); | |
1182 } | |
1183 | |
1184 return valueElement; | |
1185 }; | |
1186 | |
1187 /** | |
1188 * @param {!WebInspector.RemoteObject} object | |
1189 * @return {boolean} | |
1190 */ | |
1191 WebInspector.ObjectPropertiesSection._needsAlternateTitle = function(object) | |
1192 { | |
1193 return object && object.hasChildren && !object.customPreview() && object.sub
type !== "node" && object.type !== "function" && (object.type !== "object" || ob
ject.preview); | |
1194 }; | |
1195 | |
1196 /** | |
1197 * @param {!WebInspector.RemoteObject} func | |
1198 * @param {!Element} element | |
1199 * @param {boolean} linkify | |
1200 * @param {boolean=} includePreview | |
1201 */ | |
1202 WebInspector.ObjectPropertiesSection.formatObjectAsFunction = function(func, ele
ment, linkify, includePreview) | |
1203 { | |
1204 func.debuggerModel().functionDetailsPromise(func).then(didGetDetails); | |
1205 | |
1206 /** | |
1207 * @param {?WebInspector.DebuggerModel.FunctionDetails} response | |
1208 */ | |
1209 function didGetDetails(response) | |
1210 { | |
1211 if (!response) { | |
1212 var valueText = WebInspector.ObjectPropertiesSection.valueTextForFun
ctionDescription(func.description); | |
1213 element.createTextChild(valueText); | |
1214 return; | |
1215 } | |
1216 | |
1217 var matched = func.description.match(WebInspector.ObjectPropertiesSectio
n._functionPrefixSource); | |
1218 if (matched) { | |
1219 var prefix = createElementWithClass("span", "object-value-function-p
refix"); | |
1220 prefix.textContent = matched[0]; | |
1221 element.appendChild(prefix); | |
1222 } | |
1223 | |
1224 if (linkify && response && response.location) { | |
1225 var anchor = createElement("span"); | |
1226 element.classList.add("linkified"); | |
1227 element.appendChild(anchor); | |
1228 element.addEventListener("click", WebInspector.Revealer.reveal.bind(
WebInspector.Revealer, response.location, undefined)); | |
1229 element = anchor; | |
1230 } | |
1231 | |
1232 var text = func.description.substring(0, 200); | |
1233 if (includePreview) { | |
1234 element.createTextChild(text.replace(WebInspector.ObjectPropertiesSe
ction._functionPrefixSource, "") + (func.description.length > 200 ? "\u2026" : "
")); | |
1235 return; | |
1236 } | |
1237 | |
1238 // Now parse description and get the real params and title. | |
1239 self.runtime.extension(WebInspector.TokenizerFactory).instance().then(pr
ocessTokens); | |
1240 | |
1241 var params = null; | |
1242 var functionName = response ? response.functionName : ""; | |
1243 | |
1244 /** | |
1245 * @param {!WebInspector.TokenizerFactory} tokenizerFactory | |
1246 */ | |
1247 function processTokens(tokenizerFactory) | |
1248 { | |
1249 var tokenize = tokenizerFactory.createTokenizer("text/javascript"); | |
1250 tokenize(text, processToken); | |
1251 element.createTextChild((functionName || "anonymous") + "(" + (param
s || []).join(", ") + ")"); | |
1252 } | |
1253 | |
1254 var doneProcessing = false; | |
1255 | |
1256 /** | |
1257 * @param {string} token | |
1258 * @param {?string} tokenType | |
1259 * @param {number} column | |
1260 * @param {number} newColumn | |
1261 */ | |
1262 function processToken(token, tokenType, column, newColumn) | |
1263 { | |
1264 if (!params && tokenType === "js-def" && !functionName) | |
1265 functionName = token; | |
1266 doneProcessing = doneProcessing || token === ")"; | |
1267 if (doneProcessing) | |
1268 return; | |
1269 if (token === "(") { | |
1270 params = []; | |
1271 return; | |
1272 } | |
1273 if (params && tokenType === "js-def") | |
1274 params.push(token); | |
1275 } | |
1276 } | |
1277 }; | |
1278 | |
1279 /** | |
1280 * @constructor | |
1281 */ | |
1282 WebInspector.ObjectPropertiesSectionExpandController = function() | |
1283 { | |
1284 /** @type {!Set.<string>} */ | |
1285 this._expandedProperties = new Set(); | |
1286 }; | |
1287 | |
1288 WebInspector.ObjectPropertiesSectionExpandController._cachedPathSymbol = Symbol(
"cachedPath"); | |
1289 WebInspector.ObjectPropertiesSectionExpandController._treeOutlineId = Symbol("tr
eeOutlineId"); | |
1290 | |
1291 WebInspector.ObjectPropertiesSectionExpandController.prototype = { | |
1292 /** | |
1293 * @param {string} id | |
1294 * @param {!WebInspector.ObjectPropertiesSection} section | |
1295 */ | |
1296 watchSection: function(id, section) | |
1297 { | |
1298 section.addEventListener(TreeOutline.Events.ElementAttached, this._eleme
ntAttached, this); | |
1299 section.addEventListener(TreeOutline.Events.ElementExpanded, this._eleme
ntExpanded, this); | |
1300 section.addEventListener(TreeOutline.Events.ElementCollapsed, this._elem
entCollapsed, this); | |
1301 section[WebInspector.ObjectPropertiesSectionExpandController._treeOutlin
eId] = id; | |
1302 | |
1303 if (this._expandedProperties.has(id)) | |
1304 section.expand(); | |
1305 }, | |
1306 | |
1307 /** | |
1308 * @param {string} id | |
1309 */ | |
1310 stopWatchSectionsWithId: function(id) | |
1311 { | |
1312 for (var property of this._expandedProperties) { | |
1313 if (property.startsWith(id + ":")) | |
1314 this._expandedProperties.delete(property); | |
1315 } | |
1316 }, | |
1317 | |
1318 /** | |
1319 * @param {!WebInspector.Event} event | |
1320 */ | |
1321 _elementAttached: function(event) | |
1322 { | |
1323 var element = /** @type {!TreeElement} */ (event.data); | |
1324 if (element.isExpandable() && this._expandedProperties.has(this._propert
yPath(element))) | |
1325 element.expand(); | |
1326 }, | |
1327 | |
1328 /** | |
1329 * @param {!WebInspector.Event} event | |
1330 */ | |
1331 _elementExpanded: function(event) | |
1332 { | |
1333 var element = /** @type {!TreeElement} */ (event.data); | |
1334 this._expandedProperties.add(this._propertyPath(element)); | |
1335 }, | |
1336 | |
1337 /** | |
1338 * @param {!WebInspector.Event} event | |
1339 */ | |
1340 _elementCollapsed: function(event) | |
1341 { | |
1342 var element = /** @type {!TreeElement} */ (event.data); | |
1343 this._expandedProperties.delete(this._propertyPath(element)); | |
1344 }, | |
1345 | |
1346 /** | |
1347 * @param {!TreeElement} treeElement | |
1348 * @return {string} | |
1349 */ | |
1350 _propertyPath: function(treeElement) | |
1351 { | |
1352 var cachedPropertyPath = treeElement[WebInspector.ObjectPropertiesSectio
nExpandController._cachedPathSymbol]; | |
1353 if (cachedPropertyPath) | |
1354 return cachedPropertyPath; | |
1355 | |
1356 var current = treeElement; | |
1357 var rootElement = treeElement.treeOutline.objectTreeElement(); | |
1358 | |
1359 var result; | |
1360 | |
1361 while (current !== rootElement) { | |
1362 var currentName = ""; | |
1363 if (current.property) | |
1364 currentName = current.property.name; | |
1365 else | |
1366 currentName = typeof current.title === "string" ? current.title
: current.title.textContent; | |
1367 | |
1368 result = currentName + (result ? "." + result : ""); | |
1369 current = current.parent; | |
1370 } | |
1371 var treeOutlineId = treeElement.treeOutline[WebInspector.ObjectPropertie
sSectionExpandController._treeOutlineId]; | |
1372 result = treeOutlineId + (result ? ":" + result : ""); | |
1373 treeElement[WebInspector.ObjectPropertiesSectionExpandController._cached
PathSymbol] = result; | |
1374 return result; | |
1375 } | |
1376 }; | |
OLD | NEW |