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 {TreeOutlineInShadow}
30 * @param {!WebInspector.RemoteObject} object
31 * @param {?string|!Element=} title
32 * @param {?string=} emptyPlaceholder
33 * @param {boolean=} ignoreHasOwnProperty
34 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties
36 WebInspector.ObjectPropertiesSection = function(object, title, emptyPlaceholder, ignoreHasOwnProperty, extraProperties)
38 this._object = object;
39 this._editable = true;
40 TreeOutlineInShadow.call(this);
41 this.setFocusable(false);
42 this._objectTreeElement = new WebInspector.ObjectPropertiesSection.RootElement(object, emptyPlaceholder, ignoreHasOwnProperty, extraProperties);
43 this.appendChild(this._objectTreeElement);
44 if (typeof title === "string" || !title)
45 this.element.createChild("span").textContent = title || "";
47 this.element.appendChild(title);
49 this.element._section = this;
50 this.registerRequiredCSS("components/objectValue.css");
51 this.registerRequiredCSS("components/objectPropertiesSection.css");
52 this.rootElement().childrenListElement.classList.add("source-code", "object-properties-section")
56 WebInspector.ObjectPropertiesSection._arrayLoadThreshold = 100;
59 * @param {!WebInspector.RemoteObject} object
60 * @param {boolean=} skipProto
63 WebInspector.ObjectPropertiesSection.defaultObjectPresentation = function(object, skipProto)
65 var componentRoot = createElementWithClass("span", "source-code");
66 var shadowRoot = WebInspector.createShadowRootWithCoreStyles(componentRoot);
67 shadowRoot.appendChild(WebInspector.Widget.createStyleElement("components/objectValue.css"));
68 shadowRoot.appendChild(WebInspector.ObjectPropertiesSection.createValueElement(object, false));
69 if (!object.hasChildren)
72 var objectPropertiesSection = new WebInspector.ObjectPropertiesSection(object, componentRoot);
73 objectPropertiesSection.editable = false;
75 objectPropertiesSection.skipProto();
77 return objectPropertiesSection.element;
80 WebInspector.ObjectPropertiesSection.prototype = {
83 this._skipProto = true;
88 this._objectTreeElement.expand();
92 * @return {!TreeElement}
94 objectTreeElement: function()
96 return this._objectTreeElement;
99 enableContextMenu: function()
101 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), false);
104 _contextMenuEventFired: function(event)
106 var contextMenu = new WebInspector.ContextMenu(event);
107 contextMenu.appendApplicableItems(this._object);
111 titleLessMode: function()
113 this._objectTreeElement.listItemElement.classList.add("hidden");
114 this._objectTreeElement.childrenListElement.classList.add("title-less-mode");
115 this._objectTreeElement.expand();
118 __proto__: TreeOutlineInShadow.prototype
122 * @param {!WebInspector.RemoteObjectProperty} propertyA
123 * @param {!WebInspector.RemoteObjectProperty} propertyB
126 WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, propertyB)
128 var a = propertyA.name;
129 var b = propertyB.name;
130 if (a === "__proto__")
132 if (b === "__proto__")
134 if (propertyA.symbol && !propertyB.symbol)
136 if (propertyB.symbol && !propertyA.symbol)
138 return String.naturalOrderComparator(a, b);
143 * @extends {TreeElement}
144 * @param {!WebInspector.RemoteObject} object
145 * @param {?string=} emptyPlaceholder
146 * @param {boolean=} ignoreHasOwnProperty
147 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties
149 WebInspector.ObjectPropertiesSection.RootElement = function(object, emptyPlaceholder, ignoreHasOwnProperty, extraProperties)
151 this._object = object;
152 this._extraProperties = extraProperties || [];
153 this._ignoreHasOwnProperty = !!ignoreHasOwnProperty;
154 this._emptyPlaceholder = emptyPlaceholder;
155 var contentElement = createElement("content");
156 TreeElement.call(this, contentElement);
157 this.setExpandable(true);
158 this.selectable = false;
159 this.toggleOnClick = true;
162 WebInspector.ObjectPropertiesSection.RootElement.prototype = {
166 if (this.treeOutline)
167 this.treeOutline.element.classList.add("expanded");
170 oncollapse: function()
172 if (this.treeOutline)
173 this.treeOutline.element.classList.remove("expanded");
181 ondblclick: function(e)
186 onpopulate: function()
188 WebInspector.ObjectPropertyTreeElement._populate(this, this._object, !!this.treeOutline._skipProto, this._emptyPlaceholder, this._ignoreHasOwnProperty, this._extraProperties);
191 __proto__: TreeElement.prototype
196 * @extends {TreeElement}
197 * @param {!WebInspector.RemoteObjectProperty} property
199 WebInspector.ObjectPropertyTreeElement = function(property)
201 this.property = property;
203 // Pass an empty title, the title gets made later in onattach.
204 TreeElement.call(this);
205 this.toggleOnClick = true;
206 this.selectable = false;
209 WebInspector.ObjectPropertyTreeElement.prototype = {
210 onpopulate: function()
212 var propertyValue = /** @type {!WebInspector.RemoteObject} */ (this.property.value);
213 console.assert(propertyValue);
214 var skipProto = this.treeOutline ? this.treeOutline._skipProto : true;
215 WebInspector.ObjectPropertyTreeElement._populate(this, propertyValue, skipProto);
222 ondblclick: function(event)
224 var editableElement = this.valueElement;
225 if (!this.property.value.customPreview() && (this.property.writable || this.property.setter) && event.target.isSelfOrDescendant(editableElement))
226 this._startEditing();
236 if (this.property.value)
237 this.setExpandable(!this.property.value.customPreview() && this.property.value.hasChildren && !this.property.wasThrown);
242 this.nameElement = WebInspector.ObjectPropertiesSection.createNameElement(this.property.name);
243 if (!this.property.enumerable)
244 this.nameElement.classList.add("object-properties-section-dimmed");
245 if (this.property.isAccessorProperty())
246 this.nameElement.classList.add("properties-accessor-property-name");
247 if (this.property.symbol)
248 this.nameElement.addEventListener("contextmenu", this._contextMenuFired.bind(this, this.property.symbol), false);
250 var separatorElement = createElementWithClass("span", "object-properties-section-separator");
251 separatorElement.textContent = ": ";
253 if (this.property.value) {
254 this.valueElement = WebInspector.ObjectPropertiesSection.createValueElementWithCustomSupport(this.property.value, this.property.wasThrown, this.listItemElement);
255 this.valueElement.addEventListener("contextmenu", this._contextMenuFired.bind(this, this.property.value), false);
256 } else if (this.property.getter) {
257 this.valueElement = WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan(this.property.parentObject, [this.property.name], this._onInvokeGetterClick.bind(this));
259 this.valueElement = createElementWithClass("span", "object-value-undefined");
260 this.valueElement.textContent = WebInspector.UIString("<unreadable>");
261 this.valueElement.title = WebInspector.UIString("No property getter");
264 this.listItemElement.removeChildren();
265 this.listItemElement.appendChildren(this.nameElement, separatorElement, this.valueElement);
268 _contextMenuFired: function(value, event)
270 var contextMenu = new WebInspector.ContextMenu(event);
271 contextMenu.appendApplicableItems(value);
275 _startEditing: function()
277 if (this._prompt || !this.treeOutline._editable || this._readOnly)
280 this._editableDiv = this.listItemElement.createChild("span");
282 var text = this.property.value.description;
283 if (this.property.value.type === "string" && typeof text === "string")
284 text = "\"" + text + "\"";
286 this._editableDiv.setTextContentTruncatedIfNeeded(text, WebInspector.UIString("<string is too large to edit>"));
287 var originalContent = this._editableDiv.textContent;
289 this.valueElement.classList.add("hidden");
291 // Lie about our children to prevent expanding on double click and to collapse subproperties.
292 this.setExpandable(false);
293 this.listItemElement.classList.add("editing-sub-part");
295 this._prompt = new WebInspector.ObjectPropertyPrompt();
297 var proxyElement = this._prompt.attachAndStartEditing(this._editableDiv, this._editingCommitted.bind(this, originalContent));
298 this.listItemElement.getComponentSelection().setBaseAndExtent(this._editableDiv, 0, this._editableDiv, 1);
299 proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this, originalContent), false);
302 _editingEnded: function()
304 this._prompt.detach();
306 this._editableDiv.remove();
307 this.setExpandable(this.property.value.hasChildren && !this.property.wasThrown);
308 this.listItemElement.scrollLeft = 0;
309 this.listItemElement.classList.remove("editing-sub-part");
312 _editingCancelled: function()
314 this.valueElement.classList.remove("hidden");
315 this._editingEnded();
319 * @param {string} originalContent
321 _editingCommitted: function(originalContent)
323 var userInput = this._prompt.text();
324 if (userInput === originalContent) {
325 this._editingCancelled(); // nothing changed, so cancel
329 this._editingEnded();
330 this._applyExpression(userInput);
334 * @param {string} originalContent
335 * @param {!Event} event
337 _promptKeyDown: function(originalContent, event)
339 if (isEnterKey(event)) {
341 this._editingCommitted(originalContent);
344 if (event.keyIdentifier === "U+001B") { // Esc
346 this._editingCancelled();
352 * @param {string} expression
354 _applyExpression: function(expression)
356 var property = WebInspector.RemoteObject.toCallArgument(this.property.symbol || this.property.name);
357 expression = expression.trim();
359 this.property.parentObject.setPropertyValue(property, expression, callback.bind(this));
361 this.property.parentObject.deleteProperty(property, callback.bind(this));
364 * @param {?Protocol.Error} error
365 * @this {WebInspector.ObjectPropertyTreeElement}
367 function callback(error)
375 // The property was deleted, so remove this tree element.
376 this.parent.removeChild(this);
378 // Call updateSiblings since their value might be based on the value that just changed.
379 var parent = this.parent;
380 parent.invalidateChildren();
387 * @return {string|undefined}
389 propertyPath: function()
391 if (this._cachedPropertyPath)
392 return this._cachedPropertyPath;
398 if (current.property) {
400 result = current.property.name + "." + result;
402 result = current.property.name;
404 current = current.parent;
405 } while (current && !current.root);
407 this._cachedPropertyPath = result;
412 * @param {?WebInspector.RemoteObject} result
413 * @param {boolean=} wasThrown
415 _onInvokeGetterClick: function(result, wasThrown)
419 this.property.value = result;
420 this.property.wasThrown = wasThrown;
423 this.invalidateChildren();
426 __proto__: TreeElement.prototype
430 * @param {!TreeElement} treeElement
431 * @param {!WebInspector.RemoteObject} value
432 * @param {boolean} skipProto
433 * @param {?string=} emptyPlaceholder
434 * @param {boolean=} flattenProtoChain
435 * @param {!Array.<!WebInspector.RemoteObjectProperty>=} extraProperties
437 WebInspector.ObjectPropertyTreeElement._populate = function(treeElement, value, skipProto, emptyPlaceholder, flattenProtoChain, extraProperties)
439 if (value.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThreshold) {
440 treeElement.removeChildren();
441 WebInspector.ArrayGroupingTreeElement._populateArray(treeElement, value, 0, value.arrayLength() - 1);
446 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
447 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
449 function callback(properties, internalProperties)
451 treeElement.removeChildren();
455 extraProperties = extraProperties || [];
456 for (var i = 0; i < extraProperties.length; ++i)
457 properties.push(extraProperties[i]);
459 WebInspector.ObjectPropertyTreeElement.populateWithProperties(treeElement, properties, internalProperties,
460 skipProto, value, emptyPlaceholder);
463 if (flattenProtoChain)
464 value.getAllProperties(false, callback);
466 WebInspector.RemoteObject.loadFromObjectPerProto(value, callback);
470 * @param {!TreeElement} treeNode
471 * @param {!Array.<!WebInspector.RemoteObjectProperty>} properties
472 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
473 * @param {boolean} skipProto
474 * @param {?WebInspector.RemoteObject} value
475 * @param {?string=} emptyPlaceholder
477 WebInspector.ObjectPropertyTreeElement.populateWithProperties = function(treeNode, properties, internalProperties, skipProto, value, emptyPlaceholder) {
478 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
480 for (var i = 0; i < properties.length; ++i) {
481 var property = properties[i];
482 if (skipProto && property.name === "__proto__")
484 if (property.isAccessorProperty()) {
485 if (property.name !== "__proto__" && property.getter) {
486 property.parentObject = value;
487 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(property));
489 if (property.isOwn) {
490 if (property.getter) {
491 var getterProperty = new WebInspector.RemoteObjectProperty("get " + property.name, property.getter);
492 getterProperty.parentObject = value;
493 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(getterProperty));
495 if (property.setter) {
496 var setterProperty = new WebInspector.RemoteObjectProperty("set " + property.name, property.setter);
497 setterProperty.parentObject = value;
498 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(setterProperty));
502 property.parentObject = value;
503 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(property));
506 if (internalProperties) {
507 for (var i = 0; i < internalProperties.length; i++) {
508 internalProperties[i].parentObject = value;
509 treeNode.appendChild(new WebInspector.ObjectPropertyTreeElement(internalProperties[i]));
512 if (value && value.type === "function") {
513 // Whether function has TargetFunction internal property.
514 // This is a simple way to tell that the function is actually a bound function (we are not told).
515 // Bound function never has inner scope and doesn't need corresponding UI node.
516 var hasTargetFunction = false;
518 if (internalProperties) {
519 for (var i = 0; i < internalProperties.length; i++) {
520 if (internalProperties[i].name == "[[TargetFunction]]") {
521 hasTargetFunction = true;
526 if (!hasTargetFunction)
527 treeNode.appendChild(new WebInspector.FunctionScopeMainTreeElement(value));
529 if (value && value.type === "object" && (value.subtype === "map" || value.subtype === "set" || value.subtype === "iterator"))
530 treeNode.appendChild(new WebInspector.CollectionEntriesMainTreeElement(value));
532 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded(treeNode, emptyPlaceholder);
536 * @param {!TreeElement} treeNode
537 * @param {?string=} emptyPlaceholder
539 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded = function(treeNode, emptyPlaceholder)
541 if (treeNode.childCount())
543 var title = createElementWithClass("div", "info");
544 title.textContent = emptyPlaceholder || WebInspector.UIString("No Properties");
545 var infoElement = new TreeElement(title);
546 treeNode.appendChild(infoElement);
550 * @param {?WebInspector.RemoteObject} object
551 * @param {!Array.<string>} propertyPath
552 * @param {function(?WebInspector.RemoteObject, boolean=)} callback
555 WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan = function(object, propertyPath, callback)
557 var rootElement = createElement("span");
558 var element = rootElement.createChild("span");
559 element.textContent = WebInspector.UIString("(...)");
562 element.classList.add("object-value-calculate-value-button");
563 element.title = WebInspector.UIString("Invoke property getter");
564 element.addEventListener("click", onInvokeGetterClick, false);
566 function onInvokeGetterClick(event)
569 object.getProperty(propertyPath, callback);
577 * @extends {TreeElement}
578 * @param {!WebInspector.RemoteObject} remoteObject
580 WebInspector.FunctionScopeMainTreeElement = function(remoteObject)
582 TreeElement.call(this, "<function scope>", true);
583 this.toggleOnClick = true;
584 this.selectable = false;
585 this._remoteObject = remoteObject;
588 WebInspector.FunctionScopeMainTreeElement.prototype = {
589 onpopulate: function()
592 * @param {?WebInspector.DebuggerModel.FunctionDetails} response
593 * @this {WebInspector.FunctionScopeMainTreeElement}
595 function didGetDetails(response)
599 this.removeChildren();
601 var scopeChain = response.scopeChain || [];
602 for (var i = 0; i < scopeChain.length; ++i) {
603 var scope = scopeChain[i];
605 var isTrueObject = false;
607 switch (scope.type) {
608 case DebuggerAgent.ScopeType.Local:
609 // Not really expecting this scope type here.
610 title = WebInspector.UIString("Local");
612 case DebuggerAgent.ScopeType.Closure:
613 title = WebInspector.UIString("Closure");
615 case DebuggerAgent.ScopeType.Catch:
616 title = WebInspector.UIString("Catch");
618 case DebuggerAgent.ScopeType.Block:
619 title = WebInspector.UIString("Block");
621 case DebuggerAgent.ScopeType.Script:
622 title = WebInspector.UIString("Script");
624 case DebuggerAgent.ScopeType.With:
625 title = WebInspector.UIString("With Block");
628 case DebuggerAgent.ScopeType.Global:
629 title = WebInspector.UIString("Global");
633 console.error("Unknown scope type: " + scope.type);
637 var runtimeModel = this._remoteObject.target().runtimeModel;
639 var remoteObject = runtimeModel.createRemoteObject(scope.object);
640 var property = new WebInspector.RemoteObjectProperty(title, remoteObject);
641 property.writable = false;
642 property.parentObject = null;
643 this.appendChild(new WebInspector.ObjectPropertyTreeElement(property));
645 var scopeRef = new WebInspector.ScopeRef(i, undefined, this._remoteObject.objectId);
646 var remoteObject = runtimeModel.createScopeRemoteObject(scope.object, scopeRef);
647 var scopeTreeElement = new WebInspector.ScopeTreeElement(title, remoteObject);
648 this.appendChild(scopeTreeElement);
652 WebInspector.ObjectPropertyTreeElement._appendEmptyPlaceholderIfNeeded(this, WebInspector.UIString("No Scopes"));
655 this._remoteObject.functionDetails(didGetDetails.bind(this));
658 __proto__: TreeElement.prototype
663 * @extends {TreeElement}
664 * @param {!WebInspector.RemoteObject} remoteObject
666 WebInspector.CollectionEntriesMainTreeElement = function(remoteObject)
668 TreeElement.call(this, "<entries>", true);
669 this.toggleOnClick = true;
670 this.selectable = false;
671 this._remoteObject = remoteObject;
675 WebInspector.CollectionEntriesMainTreeElement.prototype = {
676 onpopulate: function()
679 * @param {?Array.<!DebuggerAgent.CollectionEntry>} entries
680 * @this {WebInspector.CollectionEntriesMainTreeElement}
682 function didGetCollectionEntries(entries)
686 this.removeChildren();
688 var entriesLocalObject = [];
689 var runtimeModel = this._remoteObject.target().runtimeModel;
690 for (var i = 0; i < entries.length; ++i) {
691 var entry = entries[i];
693 entriesLocalObject.push(new WebInspector.MapEntryLocalJSONObject({
694 key: runtimeModel.createRemoteObject(entry.key),
695 value: runtimeModel.createRemoteObject(entry.value)
698 entriesLocalObject.push(runtimeModel.createRemoteObject(entry.value));
701 WebInspector.ObjectPropertyTreeElement._populate(this, WebInspector.RemoteObject.fromLocalObject(entriesLocalObject), true, WebInspector.UIString("No Entries"));
702 this.title = "<entries>[" + entriesLocalObject.length + "]";
705 this._remoteObject.collectionEntries(didGetCollectionEntries.bind(this));
708 __proto__: TreeElement.prototype
713 * @extends {TreeElement}
714 * @param {string} title
715 * @param {!WebInspector.RemoteObject} remoteObject
717 WebInspector.ScopeTreeElement = function(title, remoteObject)
719 TreeElement.call(this, title, true);
720 this.toggleOnClick = true;
721 this.selectable = false;
722 this._remoteObject = remoteObject;
725 WebInspector.ScopeTreeElement.prototype = {
726 onpopulate: function()
728 WebInspector.ObjectPropertyTreeElement._populate(this, this._remoteObject, false);
731 __proto__: TreeElement.prototype
736 * @extends {TreeElement}
737 * @param {!WebInspector.RemoteObject} object
738 * @param {number} fromIndex
739 * @param {number} toIndex
740 * @param {number} propertyCount
742 WebInspector.ArrayGroupingTreeElement = function(object, fromIndex, toIndex, propertyCount)
744 TreeElement.call(this, String.sprintf("[%d \u2026 %d]", fromIndex, toIndex), true);
745 this.toggleOnClick = true;
746 this.selectable = false;
747 this._fromIndex = fromIndex;
748 this._toIndex = toIndex;
749 this._object = object;
750 this._readOnly = true;
751 this._propertyCount = propertyCount;
754 WebInspector.ArrayGroupingTreeElement._bucketThreshold = 100;
755 WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold = 250000;
756 WebInspector.ArrayGroupingTreeElement._getOwnPropertyNamesThreshold = 500000;
759 * @param {!TreeElement} treeNode
760 * @param {!WebInspector.RemoteObject} object
761 * @param {number} fromIndex
762 * @param {number} toIndex
764 WebInspector.ArrayGroupingTreeElement._populateArray = function(treeNode, object, fromIndex, toIndex)
766 WebInspector.ArrayGroupingTreeElement._populateRanges(treeNode, object, fromIndex, toIndex, true);
770 * @param {!TreeElement} treeNode
771 * @param {!WebInspector.RemoteObject} object
772 * @param {number} fromIndex
773 * @param {number} toIndex
774 * @param {boolean} topLevel
775 * @this {WebInspector.ArrayGroupingTreeElement}
777 WebInspector.ArrayGroupingTreeElement._populateRanges = function(treeNode, object, fromIndex, toIndex, topLevel)
779 object.callFunctionJSON(packRanges, [
780 { value: fromIndex },
782 { value: WebInspector.ArrayGroupingTreeElement._bucketThreshold },
783 { value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold },
784 { value: WebInspector.ArrayGroupingTreeElement._getOwnPropertyNamesThreshold }
788 * Note: must declare params as optional.
789 * @param {number=} fromIndex
790 * @param {number=} toIndex
791 * @param {number=} bucketThreshold
792 * @param {number=} sparseIterationThreshold
793 * @param {number=} getOwnPropertyNamesThreshold
794 * @suppressReceiverCheck
797 function packRanges(fromIndex, toIndex, bucketThreshold, sparseIterationThreshold, getOwnPropertyNamesThreshold)
799 var ownPropertyNames = null;
800 var consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) && ArrayBuffer.isView(this);
801 var skipGetOwnPropertyNames = consecutiveRange && (toIndex - fromIndex >= getOwnPropertyNamesThreshold);
803 function* arrayIndexes(object)
805 if (toIndex - fromIndex < sparseIterationThreshold) {
806 for (var i = fromIndex; i <= toIndex; ++i) {
811 ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(object);
812 for (var i = 0; i < ownPropertyNames.length; ++i) {
813 var name = ownPropertyNames[i];
814 var index = name >>> 0;
815 if (("" + index) === name && fromIndex <= index && index <= toIndex)
822 if (consecutiveRange) {
823 count = toIndex - fromIndex + 1;
825 for (var i of arrayIndexes(this))
829 var bucketSize = count;
830 if (count <= bucketThreshold)
833 bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
836 if (consecutiveRange) {
837 for (var i = fromIndex; i <= toIndex; i += bucketSize) {
839 var groupEnd = groupStart + bucketSize - 1;
840 if (groupEnd > toIndex)
842 ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]);
848 for (var i of arrayIndexes(this)) {
849 if (groupStart === -1)
852 if (++count === bucketSize) {
853 ranges.push([groupStart, groupEnd, count]);
859 ranges.push([groupStart, groupEnd, count]);
862 return { ranges: ranges, skipGetOwnPropertyNames: skipGetOwnPropertyNames };
865 function callback(result)
869 var ranges = /** @type {!Array.<!Array.<number>>} */ (result.ranges);
870 if (ranges.length == 1) {
871 WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeNode, object, ranges[0][0], ranges[0][1]);
873 for (var i = 0; i < ranges.length; ++i) {
874 var fromIndex = ranges[i][0];
875 var toIndex = ranges[i][1];
876 var count = ranges[i][2];
877 if (fromIndex == toIndex)
878 WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeNode, object, fromIndex, toIndex);
880 treeNode.appendChild(new WebInspector.ArrayGroupingTreeElement(object, fromIndex, toIndex, count));
884 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties(treeNode, object, result.skipGetOwnPropertyNames);
889 * @param {!TreeElement} treeNode
890 * @param {!WebInspector.RemoteObject} object
891 * @param {number} fromIndex
892 * @param {number} toIndex
893 * @this {WebInspector.ArrayGroupingTreeElement}
895 WebInspector.ArrayGroupingTreeElement._populateAsFragment = function(treeNode, object, fromIndex, toIndex)
897 object.callFunction(buildArrayFragment, [{value: fromIndex}, {value: toIndex}, {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold}], processArrayFragment.bind(this));
900 * @suppressReceiverCheck
902 * @param {number=} fromIndex // must declare optional
903 * @param {number=} toIndex // must declare optional
904 * @param {number=} sparseIterationThreshold // must declare optional
906 function buildArrayFragment(fromIndex, toIndex, sparseIterationThreshold)
908 var result = Object.create(null);
909 if (toIndex - fromIndex < sparseIterationThreshold) {
910 for (var i = fromIndex; i <= toIndex; ++i) {
915 var ownPropertyNames = Object.getOwnPropertyNames(this);
916 for (var i = 0; i < ownPropertyNames.length; ++i) {
917 var name = ownPropertyNames[i];
918 var index = name >>> 0;
919 if (String(index) === name && fromIndex <= index && index <= toIndex)
920 result[index] = this[index];
927 * @param {?WebInspector.RemoteObject} arrayFragment
928 * @param {boolean=} wasThrown
929 * @this {WebInspector.ArrayGroupingTreeElement}
931 function processArrayFragment(arrayFragment, wasThrown)
933 if (!arrayFragment || wasThrown)
935 arrayFragment.getAllProperties(false, processProperties.bind(this));
938 /** @this {WebInspector.ArrayGroupingTreeElement} */
939 function processProperties(properties, internalProperties)
944 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
945 for (var i = 0; i < properties.length; ++i) {
946 properties[i].parentObject = this._object;
947 var childTreeElement = new WebInspector.ObjectPropertyTreeElement(properties[i]);
948 childTreeElement._readOnly = true;
949 treeNode.appendChild(childTreeElement);
955 * @param {!TreeElement} treeNode
956 * @param {!WebInspector.RemoteObject} object
957 * @param {boolean} skipGetOwnPropertyNames
958 * @this {WebInspector.ArrayGroupingTreeElement}
960 WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties = function(treeNode, object, skipGetOwnPropertyNames)
962 object.callFunction(buildObjectFragment, [{value: skipGetOwnPropertyNames}], processObjectFragment.bind(this));
965 * @param {boolean=} skipGetOwnPropertyNames
966 * @suppressReceiverCheck
969 function buildObjectFragment(skipGetOwnPropertyNames)
971 var result = { __proto__: this.__proto__ };
972 if (skipGetOwnPropertyNames)
974 var names = Object.getOwnPropertyNames(this);
975 for (var i = 0; i < names.length; ++i) {
977 // Array index check according to the ES5-15.4.
978 if (String(name >>> 0) === name && name >>> 0 !== 0xffffffff)
980 var descriptor = Object.getOwnPropertyDescriptor(this, name);
982 Object.defineProperty(result, name, descriptor);
988 * @param {?WebInspector.RemoteObject} arrayFragment
989 * @param {boolean=} wasThrown
990 * @this {WebInspector.ArrayGroupingTreeElement}
992 function processObjectFragment(arrayFragment, wasThrown)
994 if (!arrayFragment || wasThrown)
996 arrayFragment.getOwnProperties(processProperties.bind(this));
1000 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
1001 * @param {?Array.<!WebInspector.RemoteObjectProperty>=} internalProperties
1002 * @this {WebInspector.ArrayGroupingTreeElement}
1004 function processProperties(properties, internalProperties)
1008 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
1009 for (var i = 0; i < properties.length; ++i) {
1010 properties[i].parentObject = this._object;
1011 var childTreeElement = new WebInspector.ObjectPropertyTreeElement(properties[i]);
1012 childTreeElement._readOnly = true;
1013 treeNode.appendChild(childTreeElement);
1018 WebInspector.ArrayGroupingTreeElement.prototype = {
1019 onpopulate: function()
1021 if (this._propertyCount >= WebInspector.ArrayGroupingTreeElement._bucketThreshold) {
1022 WebInspector.ArrayGroupingTreeElement._populateRanges(this, this._object, this._fromIndex, this._toIndex, false);
1025 WebInspector.ArrayGroupingTreeElement._populateAsFragment(this, this._object, this._fromIndex, this._toIndex);
1028 onattach: function()
1030 this.listItemElement.classList.add("object-properties-section-name");
1033 __proto__: TreeElement.prototype
1038 * @extends {WebInspector.TextPrompt}
1040 WebInspector.ObjectPropertyPrompt = function()
1042 WebInspector.TextPrompt.call(this, WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext);
1043 this.setSuggestBoxEnabled(true);
1046 WebInspector.ObjectPropertyPrompt.prototype = {
1047 __proto__: WebInspector.TextPrompt.prototype
1051 * @param {?string} name
1052 * @return {!Element}
1054 WebInspector.ObjectPropertiesSection.createNameElement = function(name)
1056 var nameElement = createElementWithClass("span", "name");
1057 if (/^\s|\s$|^$|\n/.test(name))
1058 nameElement.createTextChildren("\"", name.replace(/\n/g, "\u21B5"), "\"");
1060 nameElement.textContent = name;
1065 * @param {?string=} description
1066 * @return {string} valueText
1068 WebInspector.ObjectPropertiesSection.valueTextForFunctionDescription = function(description)
1070 var matches = /function\s([^)]*)/.exec(description);
1072 // process shorthand methods
1073 matches = /[^(]*(\([^)]*)/.exec(description);
1075 var match = matches ? matches[1] : null;
1076 return match ? match.replace(/\n/g, " ") + ")" : (description || "");
1080 * @param {!WebInspector.RemoteObject} value
1081 * @param {boolean} wasThrown
1082 * @param {!Element=} parentElement
1083 * @return {!Element}
1085 WebInspector.ObjectPropertiesSection.createValueElementWithCustomSupport = function(value, wasThrown, parentElement)
1087 if (value.customPreview()) {
1088 var result = (new WebInspector.CustomPreviewComponent(value)).element;
1089 result.classList.add("object-properties-section-custom-section");
1092 return WebInspector.ObjectPropertiesSection.createValueElement(value, wasThrown, parentElement);
1096 * @param {!WebInspector.RemoteObject} value
1097 * @param {boolean} wasThrown
1098 * @param {!Element=} parentElement
1099 * @return {!Element}
1101 WebInspector.ObjectPropertiesSection.createValueElement = function(value, wasThrown, parentElement)
1103 var valueElement = createElementWithClass("span", "value");
1104 var type = value.type;
1105 var subtype = value.subtype;
1106 var description = value.description;
1111 prefix = "[Exception: ";
1112 valueText = description;
1114 } else if (type === "string" && typeof description === "string") {
1115 // Render \n as a nice unicode cr symbol.
1117 valueText = description.replace(/\n/g, "\u21B5");
1119 } else if (type === "function") {
1120 valueText = WebInspector.ObjectPropertiesSection.valueTextForFunctionDescription(description);
1121 } else if (type !== "object" || subtype !== "node") {
1122 valueText = description;
1124 if (type !== "number" || valueText.indexOf("e") === -1) {
1125 valueElement.setTextContentTruncatedIfNeeded(valueText || "");
1127 valueElement.insertBefore(createTextNode(prefix), valueElement.firstChild);
1129 valueElement.createTextChild(suffix);
1131 var numberParts = valueText.split("e");
1132 var mantissa = valueElement.createChild("span", "object-value-scientific-notation-mantissa");
1133 mantissa.textContent = numberParts[0];
1134 var exponent = valueElement.createChild("span", "object-value-scientific-notation-exponent");
1135 exponent.textContent = "e" + numberParts[1];
1136 valueElement.classList.add("object-value-scientific-notation-number");
1137 if (parentElement) // FIXME: do it in the caller.
1138 parentElement.classList.add("hbox");
1142 valueElement.classList.add("error");
1143 if (subtype || type)
1144 valueElement.classList.add("object-value-" + (subtype || type));
1146 if (type === "object" && subtype === "node" && description) {
1147 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(valueElement, description);
1148 valueElement.addEventListener("click", mouseClick, false);
1149 valueElement.addEventListener("mousemove", mouseMove, false);
1150 valueElement.addEventListener("mouseleave", mouseLeave, false);
1152 valueElement.title = description || "";
1155 function mouseMove()
1157 WebInspector.DOMModel.highlightObjectAsDOMNode(value);
1160 function mouseLeave()
1162 WebInspector.DOMModel.hideDOMNodeHighlight();
1166 * @param {!Event} event
1168 function mouseClick(event)
1170 WebInspector.Revealer.reveal(value);
1171 event.consume(true);
1174 return valueElement;
1178 * @param {!WebInspector.RemoteObject} func
1179 * @param {!Element} element
1180 * @param {boolean} linkify
1181 * @param {boolean=} includePreview
1183 WebInspector.ObjectPropertiesSection.formatObjectAsFunction = function(func, element, linkify, includePreview)
1185 func.functionDetails(didGetDetails);
1188 * @param {?WebInspector.DebuggerModel.FunctionDetails} response
1190 function didGetDetails(response)
1193 var valueText = WebInspector.ObjectPropertiesSection.valueTextForFunctionDescription(func.description);
1194 element.createTextChild(valueText);
1198 if (linkify && response && response.location) {
1199 var anchor = createElement("span");
1200 element.classList.add("linkified");
1201 element.appendChild(anchor);
1202 element.addEventListener("click", WebInspector.Revealer.reveal.bind(WebInspector.Revealer, response.location, undefined));
1206 var text = func.description.substring(0, 200);
1207 if (includePreview) {
1208 element.textContent = text.replace(/^function /, "") + (func.description.length > 200 ? "\u2026" : "");
1212 // Now parse description and get the real params and title.
1213 self.runtime.instancePromise(WebInspector.TokenizerFactory).then(processTokens);
1216 var functionName = response ? response.functionName : "";
1219 * @param {!WebInspector.TokenizerFactory} tokenizerFactory
1221 function processTokens(tokenizerFactory)
1223 var tokenize = tokenizerFactory.createTokenizer("text/javascript");
1224 tokenize(text, processToken);
1225 element.textContent = (functionName || "anonymous") + "(" + (params || []).join(", ") + ")";
1228 var doneProcessing = false;
1231 * @param {string} token
1232 * @param {?string} tokenType
1233 * @param {number} column
1234 * @param {number} newColumn
1236 function processToken(token, tokenType, column, newColumn)
1238 if (!params && tokenType === "js-variable" && !functionName)
1239 functionName = token;
1240 doneProcessing = doneProcessing || token === ")";
1243 if (token === "(") {
1247 if (params && tokenType === "js-def")