Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(435)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/components/ObjectPropertiesSection.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698