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 = function(property, propertyPath, mode, prototypeName)
28 this._mode = mode || WebInspector.ObjectTreeView.Mode.Properties;
29 this._prototypeName = prototypeName;
31 WebInspector.ObjectTreeBaseTreeElement.call(this, property, propertyPath, property);
33 this.mainTitle = this._titleFragment();
34 this.addClassName("object-tree-property");
36 if (this.property.hasValue()) {
37 this.addClassName(this.property.value.type);
38 if (this.property.value.subtype)
39 this.addClassName(this.property.value.subtype);
41 this.addClassName("accessor");
43 if (this.property.wasThrown)
44 this.addClassName("had-error");
45 if (this.property.name === "__proto__")
46 this.addClassName("prototype-property");
48 this._updateTooltips();
49 this._updateHasChildren();
52 WebInspector.ObjectTreePropertyTreeElement.prototype = {
53 constructor: WebInspector.ObjectTreePropertyTreeElement,
54 __proto__: WebInspector.ObjectTreeBaseTreeElement.prototype,
58 onpopulate: function()
60 this._updateChildren();
65 if (this._previewView)
66 this._previewView.showTitle();
69 oncollapse: function()
71 if (this._previewView)
72 this._previewView.showPreview();
75 invokedGetter: function()
77 this.mainTitle = this._titleFragment();
79 var resolvedValue = this.resolvedValue();
80 this.addClassName(resolvedValue.type);
81 if (resolvedValue.subtype)
82 this.addClassName(resolvedValue.subtype);
84 this.addClassName("had-error");
85 this.removeClassName("accessor");
87 this._updateHasChildren();
92 _updateHasChildren: function()
94 var resolvedValue = this.resolvedValue();
95 var valueHasChildren = (resolvedValue && resolvedValue.hasChildren);
96 var wasThrown = this.hadError();
98 if (this._mode === WebInspector.ObjectTreeView.Mode.Properties)
99 this.hasChildren = !wasThrown && valueHasChildren;
101 this.hasChildren = !wasThrown && valueHasChildren && (this.property.name === "__proto__" || this._alwaysDisplayAsProperty());
104 _updateTooltips: function()
108 if (this.property.configurable)
109 attributes.push("configurable");
110 if (this.property.enumerable)
111 attributes.push("enumerable");
112 if (this.property.writable)
113 attributes.push("writable");
115 this.iconElement.title = attributes.join(" ");
118 _titleFragment: function()
120 if (this.property.name === "__proto__")
121 return this._createTitlePrototype();
123 if (this._mode === WebInspector.ObjectTreeView.Mode.Properties)
124 return this._createTitlePropertyStyle();
126 return this._createTitleAPIStyle();
129 _createTitlePrototype: function()
131 console.assert(this.property.hasValue());
132 console.assert(this.property.name === "__proto__");
134 var nameElement = document.createElement("span");
135 nameElement.className = "prototype-name";
136 nameElement.textContent = WebInspector.UIString("%s Prototype").format(this._sanitizedPrototypeString(this.property.value));
137 nameElement.title = this.propertyPathString(this.thisPropertyPath());
141 _createTitlePropertyStyle: function()
143 var container = document.createDocumentFragment();
146 var nameElement = document.createElement("span");
147 nameElement.className = "property-name";
148 nameElement.textContent = this.property.name + ": ";
149 nameElement.title = this.propertyPathString(this.thisPropertyPath());
151 // Property attributes.
152 if (this._mode === WebInspector.ObjectTreeView.Mode.Properties) {
153 if (!this.property.enumerable)
154 nameElement.classList.add("not-enumerable");
157 // Value / Getter Value / Getter.
158 var valueOrGetterElement;
159 var resolvedValue = this.resolvedValue();
161 if (resolvedValue.preview) {
162 this._previewView = new WebInspector.ObjectPreviewView(resolvedValue.preview);
163 valueOrGetterElement = this._previewView.element;
165 valueOrGetterElement = WebInspector.FormattedValue.createElementForRemoteObject(resolvedValue, this.hadError());
167 // Special case a function property string.
168 if (resolvedValue.type === "function")
169 valueOrGetterElement.textContent = this._functionPropertyString();
172 valueOrGetterElement = document.createElement("span");
173 if (this.property.hasGetter())
174 valueOrGetterElement.appendChild(this.createInteractiveGetterElement());
175 if (!this.property.hasSetter())
176 valueOrGetterElement.appendChild(this.createReadOnlyIconElement());
177 // FIXME: What if just a setter?
180 valueOrGetterElement.classList.add("value");
182 valueOrGetterElement.classList.add("error");
184 container.appendChild(nameElement);
185 container.appendChild(valueOrGetterElement);
189 _createTitleAPIStyle: function()
191 // Fixed values and special properties display like a property.
192 if (this._alwaysDisplayAsProperty())
193 return this._createTitlePropertyStyle();
195 // No API to display.
196 var isFunction = this.property.hasValue() && this.property.value.type === "function";
197 if (!isFunction && !this.property.hasGetter() && !this.property.hasSetter())
200 var container = document.createDocumentFragment();
202 // Function / Getter / Setter.
203 var nameElement = document.createElement("span");
204 nameElement.className = "property-name";
205 nameElement.textContent = this.property.name;
206 nameElement.title = this.propertyPathString(this.thisPropertyPath());
207 container.appendChild(nameElement);
210 var paramElement = document.createElement("span");
211 paramElement.className = "function-parameters";
212 paramElement.textContent = this._functionParameterString();
213 container.appendChild(paramElement);
215 if (this.property.hasGetter())
216 container.appendChild(this.createInteractiveGetterElement());
217 if (!this.property.hasSetter())
218 container.appendChild(this.createReadOnlyIconElement());
219 // FIXME: What if just a setter?
225 _alwaysDisplayAsProperty: function()
227 // Constructor, though a function, is often better treated as an expandable object.
228 if (this.property.name === "constructor")
231 // Non-function objects are often better treated as properties.
232 if (this.property.hasValue() && this.property.value.type !== "function")
235 // Fetched getter value.
236 if (this._getterValue)
242 _functionPropertyString: function()
244 return "function" + this._functionParameterString();
247 _functionParameterString: function()
249 var resolvedValue = this.resolvedValue();
250 console.assert(resolvedValue.type === "function");
252 // For Native methods, the toString is poor. We try to provide good function parameter strings.
253 if (isFunctionStringNativeCode(resolvedValue.description)) {
254 // Native function on a prototype, likely "Foo.prototype.method".
255 if (this._prototypeName) {
256 if (WebInspector.NativePrototypeFunctionParameters[this._prototypeName]) {
257 var params = WebInspector.NativePrototypeFunctionParameters[this._prototypeName][this._property.name];
258 return params ? "(" + params + ")" : "()";
262 // Native function property on a native function is likely a "Foo.method".
263 if (isFunctionStringNativeCode(this._propertyPath.object.description)) {
264 var match = this._propertyPath.object.description.match(/^function\s+([^)]+?)\(/);
267 if (WebInspector.NativeConstructorFunctionParameters[name]) {
268 var params = WebInspector.NativeConstructorFunctionParameters[name][this._property.name];
269 return params ? "(" + params + ")" : "()";
275 var match = resolvedValue.description.match(/^function.*?(\([^)]+?\))/);
276 return match ? match[1] : "()";
279 _sanitizedPrototypeString: function(value)
281 // FIXME: <https://webkit.org/b/141610> For many X, X.prototype is an X when it must be a plain object
282 if (value.type === "function")
284 if (value.subtype === "date")
286 if (value.subtype === "regexp")
289 return value.description.replace(/Prototype$/, "");
292 _updateChildren: function()
294 if (this.children.length && !this.shouldRefreshChildren)
297 var resolvedValue = this.resolvedValue();
298 if (resolvedValue.isCollectionType() && this._mode === WebInspector.ObjectTreeView.Mode.Properties)
299 resolvedValue.getCollectionEntries(0, 100, this._updateChildrenInternal.bind(this, this._updateEntries, this._mode));
300 else if (this.property.name === "__proto__")
301 resolvedValue.getOwnPropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, WebInspector.ObjectTreeView.Mode.API));
303 resolvedValue.getDisplayablePropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, this._mode));
306 _updateChildrenInternal: function(handler, mode, list)
308 this.removeChildren();
311 var errorMessageElement = WebInspector.ObjectTreeView.emptyMessageElement(WebInspector.UIString("Could not fetch properties. Object may no longer exist."));
312 this.appendChild(new TreeElement(errorMessageElement, null, false));
316 handler.call(this, list, this.resolvedValuePropertyPath(), mode);
319 _updateEntries: function(entries, propertyPath, mode)
321 for (var entry of entries) {
323 this.appendChild(new WebInspector.ObjectTreeMapKeyTreeElement(entry.key, propertyPath));
324 this.appendChild(new WebInspector.ObjectTreeMapValueTreeElement(entry.value, propertyPath, entry.key));
326 this.appendChild(new WebInspector.ObjectTreeSetIndexTreeElement(entry.value, propertyPath));
329 if (!this.children.length) {
330 var emptyMessageElement = WebInspector.ObjectTreeView.emptyMessageElement(WebInspector.UIString("No Entries."));
331 this.appendChild(new TreeElement(emptyMessageElement, null, false));
334 // Show the prototype so users can see the API.
335 var resolvedValue = this.resolvedValue();
336 resolvedValue.getOwnPropertyDescriptor("__proto__", function(propertyDescriptor) {
337 if (propertyDescriptor)
338 this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode));
342 _updateProperties: function(properties, propertyPath, mode)
344 properties.sort(WebInspector.ObjectTreeView.ComparePropertyDescriptors);
346 var resolvedValue = this.resolvedValue();
347 var isArray = resolvedValue.isArray();
348 var isPropertyMode = mode === WebInspector.ObjectTreeView.Mode.Properties || this._getterValue;
349 var isAPI = mode === WebInspector.ObjectTreeView.Mode.API;
351 var prototypeName = undefined;
352 if (this.property.name === "__proto__") {
353 if (resolvedValue.description)
354 prototypeName = this._sanitizedPrototypeString(resolvedValue);
357 for (var propertyDescriptor of properties) {
358 // FIXME: If this is a pure API ObjectTree, we should show the native getters.
359 // For now, just skip native binding getters in API mode, since we likely
360 // already showed them in the Properties section.
361 if (isAPI && propertyDescriptor.nativeGetter)
364 if (isArray && isPropertyMode) {
365 if (propertyDescriptor.isIndexProperty())
366 this.appendChild(new WebInspector.ObjectTreeArrayIndexTreeElement(propertyDescriptor, propertyPath));
367 else if (propertyDescriptor.name === "__proto__")
368 this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
370 this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
373 if (!this.children.length) {
374 var emptyMessageElement = WebInspector.ObjectTreeView.emptyMessageElement(WebInspector.UIString("No Properties."));
375 this.appendChild(new TreeElement(emptyMessageElement, null, false));