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

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

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

Powered by Google App Engine
This is Rietveld 408576698