Web Inspector: typing an expression in an iframe leads to multiple "Unsafe JavaScript...
[WebKit-https.git] / Source / WebCore / inspector / front-end / RemoteObject.js
1 /*
2  * Copyright (C) 2009 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 WebInspector.RemoteObject = function(objectId, type, subtype, value, description)
32 {
33     this._type = type;
34     this._subtype = subtype;
35     if (objectId) {
36         // handle
37         this._objectId = objectId;
38         this._description = description;
39         this._hasChildren = true;
40     } else {
41         // Primitive
42         this._description = description || (value + "");
43         this._hasChildren = false;
44     }
45 }
46
47 WebInspector.RemoteObject.fromPrimitiveValue = function(value)
48 {
49     return new WebInspector.RemoteObject(null, typeof value, null, value);
50 }
51
52 WebInspector.RemoteObject.fromLocalObject = function(value)
53 {
54     return new WebInspector.LocalJSONObject(value);
55 }
56
57 WebInspector.RemoteObject.resolveNode = function(node, objectGroup, callback)
58 {
59     function mycallback(error, object)
60     {
61         if (!callback)
62             return;
63
64         if (error || !object)
65             callback(null);
66         else
67             callback(WebInspector.RemoteObject.fromPayload(object));
68     }
69     DOMAgent.resolveNode(node.id, objectGroup, mycallback);
70 }
71
72 WebInspector.RemoteObject.fromPayload = function(payload)
73 {
74     console.assert(typeof payload === "object", "Remote object payload should only be an object");
75
76     return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description);
77 }
78
79 WebInspector.RemoteObject.type = function(remoteObject)
80 {
81     if (remoteObject === null)
82         return "null";
83
84     var type = typeof remoteObject;
85     if (type !== "object" && type !== "function")
86         return type;
87
88     return remoteObject.type;
89 }
90
91 WebInspector.RemoteObject.prototype = {
92     get objectId()
93     {
94         return this._objectId;
95     },
96
97     get type()
98     {
99         return this._type;
100     },
101
102     get subtype()
103     {
104         return this._subtype;
105     },
106
107     get description()
108     {
109         return this._description;
110     },
111
112     get hasChildren()
113     {
114         return this._hasChildren;
115     },
116
117     getOwnProperties: function(callback)
118     {
119         this._getProperties(false, callback);
120     },
121
122     getAllProperties: function(callback)
123     {
124         this._getProperties(true, callback);
125     },
126
127     _getProperties: function(ignoreHasOwnProperty, callback)
128     {
129         if (!this._objectId) {
130             callback([]);
131             return;
132         }
133         function remoteObjectBinder(error, properties)
134         {
135             if (error) {
136                 callback(null);
137                 return;
138             }
139             for (var i = 0; properties && i < properties.length; ++i)
140                 properties[i].value = WebInspector.RemoteObject.fromPayload(properties[i].value);
141             callback(properties);
142         }
143         RuntimeAgent.getProperties(this._objectId, !!ignoreHasOwnProperty, remoteObjectBinder);
144     },
145
146     setPropertyValue: function(name, value, callback)
147     {
148         if (!this._objectId) {
149             callback("Can't set a property of non-object.");
150             return;
151         }
152
153         RuntimeAgent.evaluate(value, undefined, false, true, evaluatedCallback.bind(this));
154
155         function evaluatedCallback(error, result, wasThrown)
156         {
157             if (error || wasThrown) {
158                 callback(error || result);
159                 return;
160             }
161
162             function setPropertyValue(propertyName, propertyValue)
163             {
164                 this[propertyName] = propertyValue;
165             }
166
167             delete result.description; // Optimize on traffic.
168             RuntimeAgent.callFunctionOn(this._objectId, setPropertyValue.toString(), [{ value:name }, result], propertySetCallback.bind(this));
169             if (result._objectId)
170                 RuntimeAgent.releaseObject(result._objectId);
171         }
172
173         function propertySetCallback(error, result, wasThrown) {
174             if (error || wasThrown) {
175                 callback(error || result);
176                 return;
177             }
178             callback();
179         }
180     },
181
182     pushNodeToFrontend: function(callback)
183     {
184         if (this._objectId)
185             WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback);
186         else
187             callback(0);
188     },
189
190     callFunction: function(functionDeclaration, callback)
191     {
192         function mycallback(error, result, wasThrown)
193         {
194             callback((error || wasThrown) ? null : WebInspector.RemoteObject.fromPayload(result));
195         }
196
197         RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), undefined, undefined, mycallback);
198     },
199
200     callFunctionJSON: function(functionDeclaration, callback)
201     {
202         function mycallback(error, result, wasThrown)
203         {
204             callback((error || wasThrown) ? null : result.value);
205         }
206
207         RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), undefined, true, mycallback);
208     },
209
210     release: function()
211     {
212         RuntimeAgent.releaseObject(this._objectId);
213     }
214 }
215
216 WebInspector.RemoteObjectProperty = function(name, value)
217 {
218     this.name = name;
219     this.value = value;
220 }
221
222 WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value)
223 {
224     return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value));
225 }
226
227 // The below is a wrapper around a local object that provides an interface comaptible
228 // with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection).
229 // Note that only JSON-compliant objects are currently supported, as there's no provision
230 // for traversing prototypes, extracting class names via constuctor, handling properties
231 // or functions.
232
233 WebInspector.LocalJSONObject = function(value)
234 {
235     this._value = value;
236 }
237
238 WebInspector.LocalJSONObject.prototype = {
239     get description()
240     {
241         if (this._cachedDescription)
242             return this._cachedDescription;
243
244         if (this.type === "object") {
245             switch (this.subtype) {
246             case "array":
247                 function formatArrayItem(property)
248                 {
249                     return property.value.description;
250                 }
251                 this._cachedDescription = this._concatenate("[", "]", formatArrayItem);
252                 break;
253             case "null":
254                 this._cachedDescription = "null";
255                 break;
256             default:
257                 function formatObjectItem(property)
258                 {
259                     return property.name + ":" + property.value.description;
260                 }
261                 this._cachedDescription = this._concatenate("{", "}", formatObjectItem);
262             }
263         } else
264             this._cachedDescription = String(this._value);
265
266         return this._cachedDescription;
267     },
268
269     _concatenate: function(prefix, suffix, formatProperty)
270     {
271         const previewChars = 100;
272
273         var buffer = prefix;
274         var children = this._children();
275         for (var i = 0; i < children.length; ++i) {
276             var itemDescription = formatProperty(children[i]);
277             if (buffer.length + itemDescription.length > previewChars) {
278                 buffer += ",\u2026";
279                 break;
280             }
281             if (i)
282                 buffer += ", ";
283             buffer += itemDescription;
284         }
285         buffer += suffix;
286         return buffer;
287     },
288
289     get type()
290     {
291         return typeof this._value;
292     },
293
294     get subtype()
295     {
296         if (this._value === null)
297             return "null";
298
299         if (this._value instanceof Array)
300             return "array";
301
302         return undefined;
303     },
304
305     get hasChildren()
306     {
307         return typeof this._value === "object" && this._value !== null && Object.keys(this._value).length;
308     },
309
310     getOwnProperties: function(callback)
311     {
312         callback(this._children());
313     },
314
315     getAllProperties: function(callback)
316     {
317         callback(this._children());
318     },
319
320     _children: function()
321     {
322         if (!this.hasChildren)
323             return [];
324
325         function buildProperty(propName)
326         {
327             return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName]));
328         }
329         if (!this._cachedChildren)
330             this._cachedChildren = Object.keys(this._value).map(buildProperty.bind(this));
331         return this._cachedChildren;
332     },
333
334     isError: function()
335     {
336         return false;
337     }
338 }