2bbf4351202de06a3a0faa20f66658a7d4d24948
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Protocol / 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 or null object.
42         console.assert(type !== "object" || value === null);
43         this._description = description || (value + "");
44         this._hasChildren = false;
45         this.value = value;
46     }
47 };
48
49 WebInspector.RemoteObject.fromPrimitiveValue = function(value)
50 {
51     return new WebInspector.RemoteObject(undefined, typeof value, undefined, value);
52 };
53
54 WebInspector.RemoteObject.resolveNode = function(node, objectGroup, callback)
55 {
56     function mycallback(error, object)
57     {
58         if (!callback)
59             return;
60
61         if (error || !object)
62             callback(null);
63         else
64             callback(WebInspector.RemoteObject.fromPayload(object));
65     }
66     DOMAgent.resolveNode(node.id, objectGroup, mycallback);
67 };
68
69 WebInspector.RemoteObject.fromPayload = function(payload)
70 {
71     console.assert(typeof payload === "object", "Remote object payload should only be an object");
72
73     return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description);
74 };
75
76 WebInspector.RemoteObject.type = function(remoteObject)
77 {
78     if (remoteObject === null)
79         return "null";
80
81     var type = typeof remoteObject;
82     if (type !== "object" && type !== "function")
83         return type;
84
85     return remoteObject.type;
86 };
87
88 WebInspector.RemoteObject.prototype = {
89     get objectId()
90     {
91         return this._objectId;
92     },
93
94     get type()
95     {
96         return this._type;
97     },
98
99     get subtype()
100     {
101         return this._subtype;
102     },
103
104     get description()
105     {
106         return this._description;
107     },
108
109     get hasChildren()
110     {
111         return this._hasChildren;
112     },
113
114     getOwnProperties: function(callback)
115     {
116         this._getProperties(true, false, callback);
117     },
118
119     getOwnAndGetterProperties: function(callback)
120     {
121         this._getProperties(false, true, callback);
122     },
123
124     getAllProperties: function(callback)
125     {
126         this._getProperties(false, false, callback);
127     },
128
129     _getProperties: function(ownProperties, ownAndGetterProperties, callback)
130     {
131         if (!this._objectId) {
132             callback([]);
133             return;
134         }
135
136         function remoteObjectBinder(error, properties)
137         {
138             if (error) {
139                 callback(null);
140                 return;
141             }
142             var result = [];
143             for (var i = 0; properties && i < properties.length; ++i) {
144                 var property = properties[i];
145                 if (property.get || property.set) {
146                     if (property.get)
147                         result.push(new WebInspector.RemoteObjectProperty("get " + property.name, WebInspector.RemoteObject.fromPayload(property.get), property));
148                     if (property.set)
149                         result.push(new WebInspector.RemoteObjectProperty("set " + property.name, WebInspector.RemoteObject.fromPayload(property.set), property));
150                 } else
151                     result.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value), property));
152             }
153             callback(result);
154         }
155
156         // COMPATIBILITY (iOS 8): RuntimeAgent.getProperties did not support ownerAndGetterProperties.
157         // Here we do our best to reimplement it by getting all properties and reducing them down.
158         if (ownAndGetterProperties && !RuntimeAgent.getProperties.supports("ownAndGetterProperties")) {
159             RuntimeAgent.getProperties(this._objectId, function(error, allProperties) {
160                 var ownOrGetterPropertiesList = [];
161                 if (allProperties) {
162                     for (var property of allProperties) {
163                         if (property.isOwn || property.get || property.name === "__proto__") {
164                             // Own property or getter property in prototype chain.
165                             ownOrGetterPropertiesList.push(property);
166                         } else if (property.value && property.name !== property.name.toUpperCase()) {
167                             var type = property.value.type;
168                             if (type && type !== "function" && property.name !== "constructor") {
169                                 // Possible native binding getter property converted to a value. Also, no CONSTANT name style and not "constructor".
170                                 ownOrGetterPropertiesList.push(property);
171                             }
172                         }
173                     }
174                 }
175                 remoteObjectBinder(error, ownOrGetterPropertiesList);
176             }); 
177             return;
178         }
179
180         RuntimeAgent.getProperties(this._objectId, ownProperties, ownAndGetterProperties, remoteObjectBinder);
181     },
182
183     setPropertyValue: function(name, value, callback)
184     {
185         if (!this._objectId) {
186             callback("Can't set a property of non-object.");
187             return;
188         }
189
190         RuntimeAgent.evaluate.invoke({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this));
191
192         function evaluatedCallback(error, result, wasThrown)
193         {
194             if (error || wasThrown) {
195                 callback(error || result.description);
196                 return;
197             }
198
199             function setPropertyValue(propertyName, propertyValue)
200             {
201                 this[propertyName] = propertyValue;
202             }
203
204             delete result.description; // Optimize on traffic.
205             RuntimeAgent.callFunctionOn(this._objectId, setPropertyValue.toString(), [{ value:name }, result], true, undefined, propertySetCallback.bind(this));
206             if (result._objectId)
207                 RuntimeAgent.releaseObject(result._objectId);
208         }
209
210         function propertySetCallback(error, result, wasThrown)
211         {
212             if (error || wasThrown) {
213                 callback(error || result.description);
214                 return;
215             }
216             callback();
217         }
218     },
219
220     pushNodeToFrontend: function(callback)
221     {
222         if (this._objectId)
223             WebInspector.domTreeManager.pushNodeToFrontend(this._objectId, callback);
224         else
225             callback(0);
226     },
227
228     callFunction: function(functionDeclaration, args, callback)
229     {
230         function mycallback(error, result, wasThrown)
231         {
232             callback((error || wasThrown) ? null : WebInspector.RemoteObject.fromPayload(result));
233         }
234
235         RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, mycallback);
236     },
237
238     callFunctionJSON: function(functionDeclaration, args, callback)
239     {
240         function mycallback(error, result, wasThrown)
241         {
242             callback((error || wasThrown) ? null : result.value);
243         }
244
245         RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, mycallback);
246     },
247
248     release: function()
249     {
250         RuntimeAgent.releaseObject(this._objectId);
251     },
252
253     arrayLength: function()
254     {
255         if (this.subtype !== "array")
256             return 0;
257
258         var matches = this._description.match(/\[([0-9]+)\]/);
259         if (!matches)
260             return 0;
261         return parseInt(matches[1], 10);
262     }
263 };
264
265 WebInspector.RemoteObjectProperty = function(name, value, descriptor)
266 {
267     this.name = name;
268     this.value = value;
269     this.enumerable = descriptor ? !!descriptor.enumerable : true;
270     this.writable = descriptor ? !!descriptor.writable : true;
271     if (descriptor && descriptor.wasThrown)
272         this.wasThrown = true;
273 };
274
275 WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value)
276 {
277     return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value));
278 };