2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 * @extends {WebInspector.PropertiesSection}
30 * @param {!WebInspector.RemoteObject} object
31 * @param {?string|!Element=} title
32 * @param {string=} subtitle
33 * @param {?string=} emptyPlaceholder
34 * @param {boolean=} ignoreHasOwnProperty
35 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties
36 * @param {function(new:TreeElement, !WebInspector.RemoteObjectProperty)=} treeElementConstructor
38 WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPlaceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor)
40 this._emptyPlaceholder = emptyPlaceholder;
42 this.ignoreHasOwnProperty = ignoreHasOwnProperty;
43 this.extraProperties = extraProperties;
44 this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectPropertyTreeElement;
46 this.skipProto = false;
48 WebInspector.PropertiesSection.call(this, title || "", subtitle);
51 WebInspector.ObjectPropertiesSection._arrayLoadThreshold = 100;
53 WebInspector.ObjectPropertiesSection.prototype = {
54 enableContextMenu: function()
56 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), false);
59 _contextMenuEventFired: function(event)
61 var contextMenu = new WebInspector.ContextMenu(event);
62 contextMenu.appendApplicableItems(this.object);
66 onpopulate: function()
73 if (this.object.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThreshold) {
74 this.propertiesTreeOutline.removeChildren();
75 WebInspector.ArrayGroupingTreeElement._populateArray(this.propertiesTreeOutline, this.object, 0, this.object.arrayLength() - 1);
80 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
81 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
82 * @this {WebInspector.ObjectPropertiesSection}
84 function callback(properties, internalProperties)
88 this.updateProperties(properties, internalProperties);
91 WebInspector.RemoteObject.loadFromObject(this.object, !!this.ignoreHasOwnProperty, callback.bind(this));
94 updateProperties: function(properties, internalProperties, rootTreeElementConstructor, rootPropertyComparer)
96 if (!rootTreeElementConstructor)
97 rootTreeElementConstructor = this.treeElementConstructor;
99 if (!rootPropertyComparer)
100 rootPropertyComparer = WebInspector.ObjectPropertiesSection.CompareProperties;
102 if (this.extraProperties) {
103 for (var i = 0; i < this.extraProperties.length; ++i)
104 properties.push(this.extraProperties[i]);
107 this.propertiesTreeOutline.removeChildren();
109 WebInspector.ObjectPropertyTreeElement.populateWithProperties(this.propertiesTreeOutline,
110 properties, internalProperties,
111 rootTreeElementConstructor, rootPropertyComparer,
112 this.skipProto, this.object, this._emptyPlaceholder);
114 this.propertiesForTest = properties;
117 __proto__: WebInspector.PropertiesSection.prototype
121 * @param {!WebInspector.RemoteObjectProperty} propertyA
122 * @param {!WebInspector.RemoteObjectProperty} propertyB
125 WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, propertyB)
127 var a = propertyA.name;
128 var b = propertyB.name;
129 if (a === "__proto__")
131 if (b === "__proto__")
133 if (propertyA.symbol && !propertyB.symbol)
135 if (propertyB.symbol && !propertyA.symbol)
137 return String.naturalOrderComparator(a, b);
142 * @extends {TreeElement}
143 * @param {!WebInspector.RemoteObjectProperty} property
145 WebInspector.ObjectPropertyTreeElement = function(property)
147 this.property = property;
149 // Pass an empty title, the title gets made later in onattach.
150 TreeElement.call(this, "", null, false);
151 this.toggleOnClick = true;
152 this.selectable = false;
155 WebInspector.ObjectPropertyTreeElement.prototype = {
156 onpopulate: function()
158 var propertyValue = /** @type {!WebInspector.RemoteObject} */ (this.property.value);
159 console.assert(propertyValue);
160 WebInspector.ObjectPropertyTreeElement.populate(this, propertyValue);
167 ondblclick: function(event)
169 if (this.property.writable || this.property.setter)
170 this.startEditing(event);
184 this.nameElement = document.createElementWithClass("span", "name");
185 var name = this.property.name;
186 if (/^\s|\s$|^$|\n/.test(name))
187 this.nameElement.createTextChildren("\"", name.replace(/\n/g, "\u21B5"), "\"");
189 this.nameElement.textContent = name;
190 if (!this.property.enumerable)
191 this.nameElement.classList.add("dimmed");
192 if (this.property.isAccessorProperty())
193 this.nameElement.classList.add("properties-accessor-property-name");
194 if (this.property.symbol)
195 this.nameElement.addEventListener("contextmenu", this._contextMenuFired.bind(this, this.property.symbol), false);
197 var separatorElement = document.createElementWithClass("span", "separator");
198 separatorElement.textContent = ": ";
200 if (this.property.value) {
201 this.valueElement = document.createElementWithClass("span", "value");
202 var type = this.property.value.type;
203 var subtype = this.property.value.subtype;
204 var description = this.property.value.description;
208 if (this.property.wasThrown) {
209 prefix = "[Exception: ";
210 valueText = description;
212 } else if (type === "string" && typeof description === "string") {
213 // Render \n as a nice unicode cr symbol.
215 valueText = description.replace(/\n/g, "\u21B5");
217 this.valueElement._originalTextContent = "\"" + description + "\"";
218 } else if (type === "function" && typeof description === "string") {
219 // Render function description until the first \n.
220 valueText = /.*/.exec(description)[0].replace(/\s+$/g, "");
221 this.valueElement._originalTextContent = description;
222 } else if (type !== "object" || subtype !== "node") {
223 valueText = description;
225 this.valueElement.setTextContentTruncatedIfNeeded(valueText || "");
227 this.valueElement.insertBefore(document.createTextNode(prefix), this.valueElement.firstChild);
229 this.valueElement.createTextChild(suffix);
231 if (this.property.wasThrown)
232 this.valueElement.classList.add("error");
234 this.valueElement.classList.add("console-formatted-" + (subtype || type));
236 this.valueElement.addEventListener("contextmenu", this._contextMenuFired.bind(this, this.property.value), false);
237 if (type === "object" && subtype === "node" && description) {
238 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(this.valueElement, description);
239 this.valueElement.addEventListener("mousemove", this._mouseMove.bind(this, this.property.value), false);
240 this.valueElement.addEventListener("mouseout", this._mouseOut.bind(this, this.property.value), false);
242 this.valueElement.title = description || "";
245 this.listItemElement.removeChildren();
247 this.hasChildren = this.property.value.hasChildren && !this.property.wasThrown;
249 if (this.property.getter) {
250 this.valueElement = WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan(this.property.parentObject, [this.property.name], this._onInvokeGetterClick.bind(this));
252 this.valueElement = document.createElementWithClass("span", "console-formatted-undefined");
253 this.valueElement.textContent = WebInspector.UIString("<unreadable>");
254 this.valueElement.title = WebInspector.UIString("No property getter");
258 this.listItemElement.appendChildren(this.nameElement, separatorElement, this.valueElement);
261 _contextMenuFired: function(value, event)
263 var contextMenu = new WebInspector.ContextMenu(event);
264 this.populateContextMenu(contextMenu);
265 contextMenu.appendApplicableItems(value);
270 * @param {!WebInspector.ContextMenu} contextMenu
272 populateContextMenu: function(contextMenu)
276 _mouseMove: function(event)
278 this.property.value.highlightAsDOMNode();
281 _mouseOut: function(event)
283 this.property.value.hideDOMNodeHighlight();
286 updateSiblings: function()
288 if (this.parent.root)
289 this.treeOutline.section.update();
291 this.parent.shouldRefreshChildren = true;
297 renderPromptAsBlock: function()
303 * @return {{element: !Element, value: (string|undefined)}}
305 elementAndValueToEdit: function()
308 element: this.valueElement,
309 value: (typeof this.valueElement._originalTextContent === "string") ? this.valueElement._originalTextContent : undefined
314 * @param {!Event=} event
316 startEditing: function(event)
318 var elementAndValueToEdit = this.elementAndValueToEdit();
319 var elementToEdit = elementAndValueToEdit.element;
320 var valueToEdit = elementAndValueToEdit.value;
322 if (WebInspector.isBeingEdited(elementToEdit) || !this.treeOutline.section.editable || this._readOnly)
325 // Edit original source.
326 if (typeof valueToEdit !== "undefined")
327 elementToEdit.setTextContentTruncatedIfNeeded(valueToEdit, WebInspector.UIString("<string is too large to edit>"));
329 var context = { expanded: this.expanded, elementToEdit: elementToEdit, previousContent: elementToEdit.textContent };
331 // Lie about our children to prevent expanding on double click and to collapse subproperties.
332 this.hasChildren = false;
334 this.listItemElement.classList.add("editing-sub-part");
336 this._prompt = new WebInspector.ObjectPropertyPrompt(this.renderPromptAsBlock());
339 * @this {WebInspector.ObjectPropertyTreeElement}
341 function blurListener()
343 this.editingCommitted(null, elementToEdit.textContent, context.previousContent, context);
346 var proxyElement = this._prompt.attachAndStartEditing(elementToEdit, blurListener.bind(this));
347 window.getSelection().setBaseAndExtent(elementToEdit, 0, elementToEdit, 1);
348 proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this, context), false);
354 isEditing: function()
356 return !!this._prompt;
359 editingEnded: function(context)
361 this._prompt.detach();
364 this.listItemElement.scrollLeft = 0;
365 this.listItemElement.classList.remove("editing-sub-part");
366 if (context.expanded)
370 editingCancelled: function(element, context)
372 this.editingEnded(context);
376 editingCommitted: function(element, userInput, previousContent, context)
378 if (userInput === previousContent) {
379 this.editingCancelled(element, context); // nothing changed, so cancel
383 this.editingEnded(context);
384 this.applyExpression(userInput);
387 _promptKeyDown: function(context, event)
389 if (isEnterKey(event)) {
391 this.editingCommitted(null, context.elementToEdit.textContent, context.previousContent, context);
394 if (event.keyIdentifier === "U+001B") { // Esc
396 this.editingCancelled(null, context);
402 * @param {string} expression
404 applyExpression: function(expression)
406 var property = WebInspector.RemoteObject.toCallArgument(this.property.symbol || this.property.name);
407 expression = expression.trim();
409 this.property.parentObject.setPropertyValue(property, expression, callback.bind(this));
411 this.property.parentObject.deleteProperty(property, callback.bind(this));
414 * @param {?Protocol.Error} error
415 * @this {WebInspector.ObjectPropertyTreeElement}
417 function callback(error)
425 // The property was deleted, so remove this tree element.
426 this.parent.removeChild(this);
428 // Call updateSiblings since their value might be based on the value that just changed.
429 this.updateSiblings();
435 * @return {string|undefined}
437 propertyPath: function()
439 if ("_cachedPropertyPath" in this)
440 return this._cachedPropertyPath;
446 if (current.property) {
448 result = current.property.name + "." + result;
450 result = current.property.name;
452 current = current.parent;
453 } while (current && !current.root);
455 this._cachedPropertyPath = result;
460 * @param {?WebInspector.RemoteObject} result
461 * @param {boolean=} wasThrown
463 _onInvokeGetterClick: function(result, wasThrown)
467 this.property.value = result;
468 this.property.wasThrown = wasThrown;
471 this.shouldRefreshChildren = true;
474 __proto__: TreeElement.prototype
478 * @param {!TreeElement} treeElement
479 * @param {!WebInspector.RemoteObject} value
480 * @param {string=} emptyPlaceholder
482 WebInspector.ObjectPropertyTreeElement.populate = function(treeElement, value, emptyPlaceholder) {
483 if (treeElement.children.length && !treeElement.shouldRefreshChildren)
486 if (value.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThreshold) {
487 treeElement.removeChildren();
488 WebInspector.ArrayGroupingTreeElement._populateArray(treeElement, value, 0, value.arrayLength() - 1);
493 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
494 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
496 function callback(properties, internalProperties)
498 treeElement.removeChildren();
501 WebInspector.ObjectPropertyTreeElement.populateWithProperties(treeElement, properties, internalProperties,
502 treeElement.treeOutline.section.treeElementConstructor, WebInspector.ObjectPropertiesSection.CompareProperties,
503 treeElement.treeOutline.section.skipProto, value, emptyPlaceholder);
506 WebInspector.RemoteObject.loadFromObjectPerProto(value, callback);
510 * @param {!TreeElement|!TreeOutline} treeElement
511 * @param {!Array.<!WebInspector.RemoteObjectProperty>} properties
512 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
513 * @param {function(new:TreeElement, !WebInspector.RemoteObjectProperty)} treeElementConstructor
514 * @param {function (!WebInspector.RemoteObjectProperty, !WebInspector.RemoteObjectProperty): number} comparator
515 * @param {boolean} skipProto
516 * @param {?WebInspector.RemoteObject} value
517 * @param {?string=} emptyPlaceholder
519 WebInspector.ObjectPropertyTreeElement.populateWithProperties = function(treeElement, properties, internalProperties, treeElementConstructor, comparator, skipProto, value, emptyPlaceholder) {
520 properties.sort(comparator);
522 for (var i = 0; i < properties.length; ++i) {
523 var property = properties[i];
524 if (skipProto && property.name === "__proto__")
526 if (property.isAccessorProperty()) {
527 if (property.name !== "__proto__" && property.getter) {
528 property.parentObject = value;
529 treeElement.appendChild(new treeElementConstructor(property));
531 if (property.isOwn) {
532 if (property.getter) {
533 var getterProperty = new WebInspector.RemoteObjectProperty("get " + property.name, property.getter);
534 getterProperty.parentObject = value;
535 treeElement.appendChild(new treeElementConstructor(getterProperty));
537 if (property.setter) {
538 var setterProperty = new WebInspector.RemoteObjectProperty("set " + property.name, property.setter);
539 setterProperty.parentObject = value;
540 treeElement.appendChild(new treeElementConstructor(setterProperty));
544 property.parentObject = value;
545 treeElement.appendChild(new treeElementConstructor(property));
548 if (value && value.type === "function") {
549 // Whether function has TargetFunction internal property.
550 // This is a simple way to tell that the function is actually a bound function (we are not told).
551 // Bound function never has inner scope and doesn't need corresponding UI node.
552 var hasTargetFunction = false;
554 if (internalProperties) {
555 for (var i = 0; i < internalProperties.length; i++) {
556 if (internalProperties[i].name == "[[TargetFunction]]") {
557 hasTargetFunction = true;
562 if (!hasTargetFunction)
563 treeElement.appendChild(new WebInspector.FunctionScopeMainTreeElement(value));
565 if (value && value.type === "object" && (value.subtype === "map" || value.subtype === "set"))
566 treeElement.appendChild(new WebInspector.CollectionEntriesMainTreeElement(value));
567 if (internalProperties) {
568 for (var i = 0; i < internalProperties.length; i++) {
569 internalProperties[i].parentObject = value;
570 treeElement.appendChild(new treeElementConstructor(internalProperties[i]));
574 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded(treeElement, emptyPlaceholder);
578 * @param {!TreeElement|!TreeOutline} treeElement
579 * @param {?string=} emptyPlaceholder
581 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded = function(treeElement, emptyPlaceholder)
583 if (treeElement.children.length)
585 var title = document.createElementWithClass("div", "info");
586 title.textContent = emptyPlaceholder || WebInspector.UIString("No Properties");
587 var infoElement = new TreeElement(title, null, false);
588 treeElement.appendChild(infoElement);
592 * @param {?WebInspector.RemoteObject} object
593 * @param {!Array.<string>} propertyPath
594 * @param {function(?WebInspector.RemoteObject, boolean=)} callback
597 WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan = function(object, propertyPath, callback)
599 var rootElement = document.createElement("span");
600 var element = rootElement.createChild("span");
601 element.textContent = WebInspector.UIString("(...)");
604 element.classList.add("properties-calculate-value-button");
605 element.title = WebInspector.UIString("Invoke property getter");
606 element.addEventListener("click", onInvokeGetterClick, false);
608 function onInvokeGetterClick(event)
611 object.getProperty(propertyPath, callback);
619 * @extends {TreeElement}
620 * @param {!WebInspector.RemoteObject} remoteObject
622 WebInspector.FunctionScopeMainTreeElement = function(remoteObject)
624 TreeElement.call(this, "<function scope>", null, false);
625 this.toggleOnClick = true;
626 this.selectable = false;
627 this._remoteObject = remoteObject;
628 this.hasChildren = true;
631 WebInspector.FunctionScopeMainTreeElement.prototype = {
632 onpopulate: function()
634 if (this.children.length && !this.shouldRefreshChildren)
638 * @param {?WebInspector.DebuggerModel.FunctionDetails} response
639 * @this {WebInspector.FunctionScopeMainTreeElement}
641 function didGetDetails(response)
645 this.removeChildren();
647 var scopeChain = response.scopeChain || [];
648 for (var i = 0; i < scopeChain.length; ++i) {
649 var scope = scopeChain[i];
653 switch (scope.type) {
654 case DebuggerAgent.ScopeType.Local:
655 // Not really expecting this scope type here.
656 title = WebInspector.UIString("Local");
657 isTrueObject = false;
659 case DebuggerAgent.ScopeType.Closure:
660 title = WebInspector.UIString("Closure");
661 isTrueObject = false;
663 case DebuggerAgent.ScopeType.Catch:
664 title = WebInspector.UIString("Catch");
665 isTrueObject = false;
667 case DebuggerAgent.ScopeType.With:
668 title = WebInspector.UIString("With Block");
671 case DebuggerAgent.ScopeType.Global:
672 title = WebInspector.UIString("Global");
676 console.error("Unknown scope type: " + scope.type);
680 var runtimeModel = this._remoteObject.target().runtimeModel;
682 var remoteObject = runtimeModel.createRemoteObject(scope.object);
683 var property = new WebInspector.RemoteObjectProperty(title, remoteObject);
684 property.writable = false;
685 property.parentObject = null;
686 this.appendChild(new this.treeOutline.section.treeElementConstructor(property));
688 var scopeRef = new WebInspector.ScopeRef(i, undefined, this._remoteObject.objectId);
689 var remoteObject = runtimeModel.createScopeRemoteObject(scope.object, scopeRef);
690 var scopeTreeElement = new WebInspector.ScopeTreeElement(title, remoteObject);
691 this.appendChild(scopeTreeElement);
695 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded(this, WebInspector.UIString("No Scopes"));
698 this._remoteObject.functionDetails(didGetDetails.bind(this));
701 __proto__: TreeElement.prototype
706 * @extends {TreeElement}
707 * @param {!WebInspector.RemoteObject} remoteObject
709 WebInspector.CollectionEntriesMainTreeElement = function(remoteObject)
711 TreeElement.call(this, "<entries>", null, false);
712 this.toggleOnClick = true;
713 this.selectable = false;
714 this._remoteObject = remoteObject;
715 this.hasChildren = true;
718 WebInspector.CollectionEntriesMainTreeElement.prototype = {
719 onpopulate: function()
721 if (this.children.length && !this.shouldRefreshChildren)
725 * @param {?Array.<!DebuggerAgent.CollectionEntry>} entries
726 * @this {WebInspector.CollectionEntriesMainTreeElement}
728 function didGetCollectionEntries(entries)
732 this.removeChildren();
734 var entriesLocalObject = [];
735 var runtimeModel = this._remoteObject.target().runtimeModel;
736 for (var i = 0; i < entries.length; ++i) {
737 var entry = entries[i];
739 entriesLocalObject.push(new WebInspector.MapEntryLocalJSONObject({
740 key: runtimeModel.createRemoteObject(entry.key),
741 value: runtimeModel.createRemoteObject(entry.value)
744 entriesLocalObject.push(runtimeModel.createRemoteObject(entry.value));
747 WebInspector.ObjectPropertyTreeElement.populate(this, WebInspector.RemoteObject.fromLocalObject(entriesLocalObject), WebInspector.UIString("No Entries"));
748 this.title = "<entries>[" + entriesLocalObject.length + "]";
751 this._remoteObject.collectionEntries(didGetCollectionEntries.bind(this));
754 __proto__: TreeElement.prototype
759 * @extends {TreeElement}
760 * @param {string} title
761 * @param {!WebInspector.RemoteObject} remoteObject
763 WebInspector.ScopeTreeElement = function(title, remoteObject)
765 TreeElement.call(this, title, null, false);
766 this.toggleOnClick = true;
767 this.selectable = false;
768 this._remoteObject = remoteObject;
769 this.hasChildren = true;
772 WebInspector.ScopeTreeElement.prototype = {
773 onpopulate: function()
775 WebInspector.ObjectPropertyTreeElement.populate(this, this._remoteObject);
778 __proto__: TreeElement.prototype
783 * @extends {TreeElement}
784 * @param {!WebInspector.RemoteObject} object
785 * @param {number} fromIndex
786 * @param {number} toIndex
787 * @param {number} propertyCount
789 WebInspector.ArrayGroupingTreeElement = function(object, fromIndex, toIndex, propertyCount)
791 TreeElement.call(this, String.sprintf("[%d \u2026 %d]", fromIndex, toIndex), undefined, true);
792 this.toggleOnClick = true;
793 this.selectable = false;
794 this._fromIndex = fromIndex;
795 this._toIndex = toIndex;
796 this._object = object;
797 this._readOnly = true;
798 this._propertyCount = propertyCount;
799 this._populated = false;
802 WebInspector.ArrayGroupingTreeElement._bucketThreshold = 100;
803 WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold = 250000;
806 * @param {!TreeElement|!TreeOutline} treeElement
807 * @param {!WebInspector.RemoteObject} object
808 * @param {number} fromIndex
809 * @param {number} toIndex
811 WebInspector.ArrayGroupingTreeElement._populateArray = function(treeElement, object, fromIndex, toIndex)
813 WebInspector.ArrayGroupingTreeElement._populateRanges(treeElement, object, fromIndex, toIndex, true);
817 * @param {!TreeElement|!TreeOutline} treeElement
818 * @param {!WebInspector.RemoteObject} object
819 * @param {number} fromIndex
820 * @param {number} toIndex
821 * @param {boolean} topLevel
822 * @this {WebInspector.ArrayGroupingTreeElement}
824 WebInspector.ArrayGroupingTreeElement._populateRanges = function(treeElement, object, fromIndex, toIndex, topLevel)
826 object.callFunctionJSON(packRanges, [{value: fromIndex}, {value: toIndex}, {value: WebInspector.ArrayGroupingTreeElement._bucketThreshold}, {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold}], callback);
829 * @suppressReceiverCheck
831 * @param {number=} fromIndex // must declare optional
832 * @param {number=} toIndex // must declare optional
833 * @param {number=} bucketThreshold // must declare optional
834 * @param {number=} sparseIterationThreshold // must declare optional
836 function packRanges(fromIndex, toIndex, bucketThreshold, sparseIterationThreshold)
838 var ownPropertyNames = null;
843 function doLoop(iterationCallback)
845 if (toIndex - fromIndex < sparseIterationThreshold) {
846 for (var i = fromIndex; i <= toIndex; ++i) {
848 iterationCallback(i);
851 ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(this);
852 for (var i = 0; i < ownPropertyNames.length; ++i) {
853 var name = ownPropertyNames[i];
854 var index = name >>> 0;
855 if (String(index) === name && fromIndex <= index && index <= toIndex)
856 iterationCallback(index);
862 function countIterationCallback()
866 doLoop.call(this, countIterationCallback);
868 var bucketSize = count;
869 if (count <= bucketThreshold)
872 bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
878 function loopIterationCallback(i)
880 if (groupStart === -1)
884 if (++count === bucketSize) {
885 ranges.push([groupStart, groupEnd, count]);
890 doLoop.call(this, loopIterationCallback);
893 ranges.push([groupStart, groupEnd, count]);
897 function callback(ranges)
899 if (ranges.length == 1)
900 WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeElement, object, ranges[0][0], ranges[0][1]);
902 for (var i = 0; i < ranges.length; ++i) {
903 var fromIndex = ranges[i][0];
904 var toIndex = ranges[i][1];
905 var count = ranges[i][2];
906 if (fromIndex == toIndex)
907 WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeElement, object, fromIndex, toIndex);
909 treeElement.appendChild(new WebInspector.ArrayGroupingTreeElement(object, fromIndex, toIndex, count));
913 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties(treeElement, object);
918 * @param {!TreeElement|!TreeOutline} treeElement
919 * @param {!WebInspector.RemoteObject} object
920 * @param {number} fromIndex
921 * @param {number} toIndex
922 * @this {WebInspector.ArrayGroupingTreeElement}
924 WebInspector.ArrayGroupingTreeElement._populateAsFragment = function(treeElement, object, fromIndex, toIndex)
926 object.callFunction(buildArrayFragment, [{value: fromIndex}, {value: toIndex}, {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold}], processArrayFragment.bind(this));
929 * @suppressReceiverCheck
931 * @param {number=} fromIndex // must declare optional
932 * @param {number=} toIndex // must declare optional
933 * @param {number=} sparseIterationThreshold // must declare optional
935 function buildArrayFragment(fromIndex, toIndex, sparseIterationThreshold)
937 var result = Object.create(null);
938 if (toIndex - fromIndex < sparseIterationThreshold) {
939 for (var i = fromIndex; i <= toIndex; ++i) {
944 var ownPropertyNames = Object.getOwnPropertyNames(this);
945 for (var i = 0; i < ownPropertyNames.length; ++i) {
946 var name = ownPropertyNames[i];
947 var index = name >>> 0;
948 if (String(index) === name && fromIndex <= index && index <= toIndex)
949 result[index] = this[index];
956 * @param {?WebInspector.RemoteObject} arrayFragment
957 * @param {boolean=} wasThrown
958 * @this {WebInspector.ArrayGroupingTreeElement}
960 function processArrayFragment(arrayFragment, wasThrown)
962 if (!arrayFragment || wasThrown)
964 arrayFragment.getAllProperties(false, processProperties.bind(this));
967 /** @this {WebInspector.ArrayGroupingTreeElement} */
968 function processProperties(properties, internalProperties)
973 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
974 for (var i = 0; i < properties.length; ++i) {
975 properties[i].parentObject = this._object;
976 var childTreeElement = new treeElement.treeOutline.section.treeElementConstructor(properties[i]);
977 childTreeElement._readOnly = true;
978 treeElement.appendChild(childTreeElement);
984 * @param {!TreeElement|!TreeOutline} treeElement
985 * @param {!WebInspector.RemoteObject} object
986 * @this {WebInspector.ArrayGroupingTreeElement}
988 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties = function(treeElement, object)
990 object.callFunction(buildObjectFragment, undefined, processObjectFragment.bind(this));
993 * @suppressReceiverCheck
996 function buildObjectFragment()
998 var result = Object.create(this.__proto__);
999 var names = Object.getOwnPropertyNames(this);
1000 for (var i = 0; i < names.length; ++i) {
1001 var name = names[i];
1002 // Array index check according to the ES5-15.4.
1003 if (String(name >>> 0) === name && name >>> 0 !== 0xffffffff)
1005 var descriptor = Object.getOwnPropertyDescriptor(this, name);
1007 Object.defineProperty(result, name, descriptor);
1013 * @param {?WebInspector.RemoteObject} arrayFragment
1014 * @param {boolean=} wasThrown
1015 * @this {WebInspector.ArrayGroupingTreeElement}
1017 function processObjectFragment(arrayFragment, wasThrown)
1019 if (!arrayFragment || wasThrown)
1021 arrayFragment.getOwnProperties(processProperties.bind(this));
1025 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
1026 * @param {?Array.<!WebInspector.RemoteObjectProperty>=} internalProperties
1027 * @this {WebInspector.ArrayGroupingTreeElement}
1029 function processProperties(properties, internalProperties)
1033 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
1034 for (var i = 0; i < properties.length; ++i) {
1035 properties[i].parentObject = this._object;
1036 var childTreeElement = new treeElement.treeOutline.section.treeElementConstructor(properties[i]);
1037 childTreeElement._readOnly = true;
1038 treeElement.appendChild(childTreeElement);
1043 WebInspector.ArrayGroupingTreeElement.prototype = {
1044 onpopulate: function()
1046 if (this._populated)
1049 this._populated = true;
1051 if (this._propertyCount >= WebInspector.ArrayGroupingTreeElement._bucketThreshold) {
1052 WebInspector.ArrayGroupingTreeElement._populateRanges(this, this._object, this._fromIndex, this._toIndex, false);
1055 WebInspector.ArrayGroupingTreeElement._populateAsFragment(this, this._object, this._fromIndex, this._toIndex);
1058 onattach: function()
1060 this.listItemElement.classList.add("name");
1063 __proto__: TreeElement.prototype
1068 * @extends {WebInspector.TextPrompt}
1069 * @param {boolean=} renderAsBlock
1071 WebInspector.ObjectPropertyPrompt = function(renderAsBlock)
1073 WebInspector.TextPrompt.call(this, WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext);
1074 this.setSuggestBoxEnabled(true);
1076 this.renderAsBlock();
1079 WebInspector.ObjectPropertyPrompt.prototype = {
1080 __proto__: WebInspector.TextPrompt.prototype