2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
26 WebInspector.ObjectTreePropertyTreeElement = class ObjectTreePropertyTreeElement extends WebInspector.ObjectTreeBaseTreeElement
28 constructor(property, propertyPath, mode, prototypeName)
30 super(property, propertyPath, property);
32 this._mode = mode || WebInspector.ObjectTreeView.Mode.Properties;
33 this._prototypeName = prototypeName;
35 this.mainTitle = this._titleFragment();
36 this.addClassName("object-tree-property");
38 if (this.property.hasValue()) {
39 this.addClassName(this.property.value.type);
40 if (this.property.value.subtype)
41 this.addClassName(this.property.value.subtype);
43 this.addClassName("accessor");
45 if (this.property.wasThrown)
46 this.addClassName("had-error");
47 if (this.property.name === "__proto__")
48 this.addClassName("prototype-property");
50 this._updateTooltips();
51 this._updateHasChildren();
58 this._updateChildren();
63 if (this._previewView)
64 this._previewView.showTitle();
69 if (this._previewView)
70 this._previewView.showPreview();
75 this.mainTitle = this._titleFragment();
77 var resolvedValue = this.resolvedValue();
78 this.addClassName(resolvedValue.type);
79 if (resolvedValue.subtype)
80 this.addClassName(resolvedValue.subtype);
82 this.addClassName("had-error");
83 this.removeClassName("accessor");
85 this._updateHasChildren();
92 var resolvedValue = this.resolvedValue();
93 var valueHasChildren = (resolvedValue && resolvedValue.hasChildren);
94 var wasThrown = this.hadError();
96 if (this._mode === WebInspector.ObjectTreeView.Mode.Properties)
97 this.hasChildren = !wasThrown && valueHasChildren;
99 this.hasChildren = !wasThrown && valueHasChildren && (this.property.name === "__proto__" || this._alwaysDisplayAsProperty());
106 if (this.property.configurable)
107 attributes.push("configurable");
108 if (this.property.enumerable)
109 attributes.push("enumerable");
110 if (this.property.writable)
111 attributes.push("writable");
113 this.iconElement.title = attributes.join(" ");
118 if (this.property.name === "__proto__")
119 return this._createTitlePrototype();
121 if (this._mode === WebInspector.ObjectTreeView.Mode.Properties)
122 return this._createTitlePropertyStyle();
124 return this._createTitleAPIStyle();
127 _createTitlePrototype()
129 console.assert(this.property.hasValue());
130 console.assert(this.property.name === "__proto__");
132 var nameElement = document.createElement("span");
133 nameElement.className = "prototype-name";
134 nameElement.textContent = WebInspector.UIString("%s Prototype").format(this._sanitizedPrototypeString(this.property.value));
135 nameElement.title = this.propertyPathString(this.thisPropertyPath());
139 _createTitlePropertyStyle()
141 var container = document.createDocumentFragment();
144 var nameElement = document.createElement("span");
145 nameElement.className = "property-name";
146 nameElement.textContent = this.property.name + ": ";
147 nameElement.title = this.propertyPathString(this.thisPropertyPath());
149 // Property attributes.
150 if (this._mode === WebInspector.ObjectTreeView.Mode.Properties) {
151 if (!this.property.enumerable)
152 nameElement.classList.add("not-enumerable");
155 // Value / Getter Value / Getter.
156 var valueOrGetterElement;
157 var resolvedValue = this.resolvedValue();
159 if (resolvedValue.preview) {
160 this._previewView = new WebInspector.ObjectPreviewView(resolvedValue.preview);
161 valueOrGetterElement = this._previewView.element;
163 valueOrGetterElement = WebInspector.FormattedValue.createElementForRemoteObject(resolvedValue, this.hadError());
165 // Special case a function property string.
166 if (resolvedValue.type === "function")
167 valueOrGetterElement.textContent = this._functionPropertyString();
170 valueOrGetterElement = document.createElement("span");
171 if (this.property.hasGetter())
172 valueOrGetterElement.appendChild(this.createInteractiveGetterElement());
173 if (!this.property.hasSetter())
174 valueOrGetterElement.appendChild(this.createReadOnlyIconElement());
175 // FIXME: What if just a setter?
178 valueOrGetterElement.classList.add("value");
180 valueOrGetterElement.classList.add("error");
182 container.appendChild(nameElement);
183 container.appendChild(valueOrGetterElement);
187 _createTitleAPIStyle()
189 // Fixed values and special properties display like a property.
190 if (this._alwaysDisplayAsProperty())
191 return this._createTitlePropertyStyle();
193 // No API to display.
194 var isFunction = this.property.hasValue() && this.property.value.type === "function";
195 if (!isFunction && !this.property.hasGetter() && !this.property.hasSetter())
198 var container = document.createDocumentFragment();
200 // Function / Getter / Setter.
201 var nameElement = document.createElement("span");
202 nameElement.className = "property-name";
203 nameElement.textContent = this.property.name;
204 nameElement.title = this.propertyPathString(this.thisPropertyPath());
205 container.appendChild(nameElement);
208 var paramElement = document.createElement("span");
209 paramElement.className = "function-parameters";
210 paramElement.textContent = this._functionParameterString();
211 container.appendChild(paramElement);
213 if (this.property.hasGetter())
214 container.appendChild(this.createInteractiveGetterElement());
215 if (!this.property.hasSetter())
216 container.appendChild(this.createReadOnlyIconElement());
217 // FIXME: What if just a setter?
223 _alwaysDisplayAsProperty()
225 // Constructor, though a function, is often better treated as an expandable object.
226 if (this.property.name === "constructor")
229 // Non-function objects are often better treated as properties.
230 if (this.property.hasValue() && this.property.value.type !== "function")
233 // Fetched getter value.
234 if (this._getterValue)
240 _functionPropertyString()
242 return "function" + this._functionParameterString();
245 _functionParameterString()
247 var resolvedValue = this.resolvedValue();
248 console.assert(resolvedValue.type === "function");
250 // For Native methods, the toString is poor. We try to provide good function parameter strings.
251 if (isFunctionStringNativeCode(resolvedValue.description)) {
252 // Native function on a prototype, likely "Foo.prototype.method".
253 if (this._prototypeName) {
254 if (WebInspector.NativePrototypeFunctionParameters[this._prototypeName]) {
255 var params = WebInspector.NativePrototypeFunctionParameters[this._prototypeName][this._property.name];
256 return params ? "(" + params + ")" : "()";
260 // Native function property on a native function is likely a "Foo.method".
261 if (isFunctionStringNativeCode(this._propertyPath.object.description)) {
262 var match = this._propertyPath.object.description.match(/^function\s+([^)]+?)\(/);
265 if (WebInspector.NativeConstructorFunctionParameters[name]) {
266 var params = WebInspector.NativeConstructorFunctionParameters[name][this._property.name];
267 return params ? "(" + params + ")" : "()";
272 // Native DOM constructor.
273 if (this._propertyPath.object.description.endsWith("Constructor")) {
274 var name = this._propertyPath.object.description;
275 if (WebInspector.NativeConstructorFunctionParameters[name]) {
276 var params = WebInspector.NativeConstructorFunctionParameters[name][this._property.name];
277 return params ? "(" + params + ")" : "()";
282 var match = resolvedValue.description.match(/^function.*?(\([^)]+?\))/);
283 return match ? match[1] : "()";
286 _sanitizedPrototypeString(value)
288 // FIXME: <https://webkit.org/b/141610> For many X, X.prototype is an X when it must be a plain object
289 if (value.type === "function")
291 if (value.subtype === "date")
293 if (value.subtype === "regexp")
296 return value.description.replace(/Prototype$/, "");
301 if (this.children.length && !this.shouldRefreshChildren)
304 var resolvedValue = this.resolvedValue();
305 if (resolvedValue.isCollectionType() && this._mode === WebInspector.ObjectTreeView.Mode.Properties)
306 resolvedValue.getCollectionEntries(0, 100, this._updateChildrenInternal.bind(this, this._updateEntries, this._mode));
307 else if (this.property.name === "__proto__")
308 resolvedValue.getOwnPropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, WebInspector.ObjectTreeView.Mode.API));
310 resolvedValue.getDisplayablePropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, this._mode));
313 _updateChildrenInternal(handler, mode, list)
315 this.removeChildren();
318 var errorMessageElement = WebInspector.ObjectTreeView.createEmptyMessageElement(WebInspector.UIString("Could not fetch properties. Object may no longer exist."));
319 this.appendChild(new WebInspector.TreeElement(errorMessageElement, null, false));
323 handler.call(this, list, this.resolvedValuePropertyPath(), mode);
326 _updateEntries(entries, propertyPath, mode)
328 for (var entry of entries) {
330 this.appendChild(new WebInspector.ObjectTreeMapKeyTreeElement(entry.key, propertyPath));
331 this.appendChild(new WebInspector.ObjectTreeMapValueTreeElement(entry.value, propertyPath, entry.key));
333 this.appendChild(new WebInspector.ObjectTreeSetIndexTreeElement(entry.value, propertyPath));
336 if (!this.children.length) {
337 var emptyMessageElement = WebInspector.ObjectTreeView.createEmptyMessageElement(WebInspector.UIString("No Entries."));
338 this.appendChild(new WebInspector.TreeElement(emptyMessageElement, null, false));
341 // Show the prototype so users can see the API.
342 var resolvedValue = this.resolvedValue();
343 resolvedValue.getOwnPropertyDescriptor("__proto__", function(propertyDescriptor) {
344 if (propertyDescriptor)
345 this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode));
349 _updateProperties(properties, propertyPath, mode)
351 properties.sort(WebInspector.ObjectTreeView.comparePropertyDescriptors);
353 var resolvedValue = this.resolvedValue();
354 var isArray = resolvedValue.isArray();
355 var isPropertyMode = mode === WebInspector.ObjectTreeView.Mode.Properties || this._getterValue;
356 var isAPI = mode === WebInspector.ObjectTreeView.Mode.API;
358 var prototypeName = undefined;
359 if (this.property.name === "__proto__") {
360 if (resolvedValue.description)
361 prototypeName = this._sanitizedPrototypeString(resolvedValue);
364 for (var propertyDescriptor of properties) {
365 // FIXME: If this is a pure API ObjectTree, we should show the native getters.
366 // For now, just skip native binding getters in API mode, since we likely
367 // already showed them in the Properties section.
368 if (isAPI && propertyDescriptor.nativeGetter)
371 if (isArray && isPropertyMode) {
372 if (propertyDescriptor.isIndexProperty())
373 this.appendChild(new WebInspector.ObjectTreeArrayIndexTreeElement(propertyDescriptor, propertyPath));
374 else if (propertyDescriptor.name === "__proto__")
375 this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
377 this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
380 if (!this.children.length) {
381 var emptyMessageElement = WebInspector.ObjectTreeView.createEmptyMessageElement(WebInspector.UIString("No Properties."));
382 this.appendChild(new WebInspector.TreeElement(emptyMessageElement, null, false));