Web Inspector: Create Separate Model and View Objects for RemoteObjects / ObjectPrevi...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ObjectTreePropertyTreeElement.js
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 WebInspector.ObjectTreePropertyTreeElement = function(property, mode)
27 {
28     console.assert(property instanceof WebInspector.PropertyDescriptor);
29
30     this._property = property;
31     this._mode = mode || WebInspector.ObjectTreeView.Mode.Properties;
32
33     // FIXME: API Mode not turned on yet.
34     this._mode = WebInspector.ObjectTreeView.Mode.Properties;
35
36     TreeElement.call(this, "", null, false);
37     this.toggleOnClick = true;
38     this.selectable = false;
39 };
40
41 WebInspector.ObjectTreePropertyTreeElement.prototype = {
42     constructor: WebInspector.ObjectTreePropertyTreeElement,
43     __proto__: TreeElement.prototype,
44
45     // Public
46
47     get property()
48     {
49         return this._property;
50     },
51
52     // Protected
53
54     onpopulate: function()
55     {
56         this._updateChildren();
57     },
58
59     onattach: function()
60     {
61         this.listItemElement.classList.add("object-tree-property");
62
63         this._updateTitle();
64     },
65
66     // Private
67
68     _updateTitle: function()
69     {
70         this.listItemElement.removeChildren();
71
72         if (this._mode === WebInspector.ObjectTreeView.Mode.Properties) {
73             this._updateTitlePropertyStyle();
74             this.hasChildren = this._property.hasValue() && this._property.value.hasChildren && !this._property.wasThrown;
75         } else {
76             this._updateTitleAPIStyle();
77             this.hasChildren = this._property.hasValue() && this._property.value.hasChildren && !this._property.wasThrown && this._property.name === "__proto__";
78         }
79     },
80
81     _updateTitlePropertyStyle: function()
82     {
83         // Property name.
84         var nameElement = document.createElement("span");
85         nameElement.className = "name";
86         nameElement.textContent = this._property.name;
87
88         // Property attributes.
89         if (this._mode === WebInspector.ObjectTreeView.Mode.Properties) {
90             if (!this._property.enumerable)
91                 nameElement.classList.add("not-enumerable");
92         }
93
94         // Separator.
95         var separatorElement = document.createElement("span");
96         separatorElement.className = "separator";
97         separatorElement.textContent = ": ";
98
99         // Value / Getter.
100         var valueOrGetterElement = document.createElement("span");
101         valueOrGetterElement.className = "value";
102
103         if (this._property.hasValue()) {
104             valueOrGetterElement.textContent = this._descriptionString();
105             valueOrGetterElement.classList.add(WebInspector.ObjectTreeView.classNameForObject(this._property.value));
106             // FIXME: Context Menu for Value. (See ObjectPropertiesSection).
107             // FIXME: Option+Click for Value.
108         } else {
109             console.assert(this._property.hasGetter());
110             valueOrGetterElement.textContent = "(...)";
111             // FIXME: Click to Populate Value.
112             // FIXME: Context Menu to Populate Value.
113         }
114
115         if (this._property.wasThrown)
116             valueOrGetterElement.classList.add("error");
117
118         this.listItemElement.appendChild(nameElement);
119         this.listItemElement.appendChild(separatorElement);
120         this.listItemElement.appendChild(valueOrGetterElement);
121     },
122
123     _updateTitleAPIStyle: function()
124     {
125         // Fixed value. Display like a property.
126         const propertyNamesToDisplayAsValues = ["__proto__", "constructor"];
127         if (propertyNamesToDisplayAsValues.contains(this._property.name) || (this._property.hasValue() && this._property.value.type !== "function")) {
128             this._updateTitlePropertyStyle();
129             return;
130         }
131
132         // No API to display.
133         var isFunction = this._property.hasValue() && this._property.value.type === "function";
134         if (!isFunction && !this._property.hasGetter() && !this._property.hasSetter())
135             return;
136
137         // Function / Getter / Setter.
138         var nameElement = document.createElement("span");
139         nameElement.className = "name";
140         nameElement.textContent = this._property.name;
141         this.listItemElement.appendChild(nameElement);
142
143         if (isFunction) {
144             var paramElement = document.createElement("span");
145             paramElement.textContent = this._functionParameterString();
146             this.listItemElement.appendChild(paramElement);
147         }
148
149         if (this._property.hasGetter()) {
150             var icon = document.createElement("span");
151             icon.textContent += "[G]";
152             this.listItemElement.appendChild(icon);
153         }
154         if (this._property.hasSetter()) {
155             var icon = document.createElement("span");
156             icon.textContent += "[S]";
157             this.listItemElement.appendChild(icon);
158         }
159     },
160
161     _descriptionString: function()
162     {
163         var value = this._property.value;
164         var description = value.description;
165
166         // Exception.
167         if (this._property.wasThrown)
168             return "[Exception: " + description + "]";
169
170         // String: replace newlines as nice unicode symbols.
171         if (value.type === "string")
172             return "\"" + description.replace(/\n/g, "\u21B5").replace(/"/g, "\\\"") + "\"";
173
174         // Function: Collapse whitespace in function display strings.
175         if (value.type === "function")
176             return /.*/.exec(description)[0].replace(/ +$/g, "");
177
178         return description;
179     },
180
181     _functionParameterString: function()
182     {
183         console.assert(this._property.value.type === "function");
184
185         var match = this._property.value.description.match(/^function.*?(\([^)]+?\))/);
186         return match ? match[1] : "()";
187     },
188
189     _updateChildren: function()
190     {
191         if (this.children.length && !this.shouldRefreshChildren)
192             return;
193
194         function callback(mode, properties)
195         {
196             this.removeChildren();
197
198             if (properties) {
199                 properties.sort(WebInspector.ObjectTreeView.ComparePropertyDescriptors);
200                 for (var propertyDescriptor of properties)
201                     this.appendChild(new WebInspector.ObjectTreePropertyTreeElement(propertyDescriptor, mode));
202             }
203
204             if (mode === WebInspector.ObjectTreeView.Mode.Properties) {
205                 if (this._property.value.isCollectionType())
206                     this.appendChild(new WebInspector.ObjectTreeCollectionTreeElement(this._property.value));
207             }
208         };
209
210         if (this._property.name === "__proto__")
211             this._property.value.getOwnPropertyDescriptors(callback.bind(this, WebInspector.ObjectTreeView.Mode.API));
212         else
213             this._property.value.getOwnAndGetterPropertyDescriptors(callback.bind(this, this._mode));
214     }
215 };