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
31WebInspector.RemoteObject = function(objectId, type, description, hasChildren)
32{
33    this._objectId = objectId;
34    this._type = type;
35    this._description = description;
36    this._hasChildren = hasChildren;
37}
38
39WebInspector.RemoteObject.fromPrimitiveValue = function(value)
40{
41    return new WebInspector.RemoteObject(null, typeof value, value);
42}
43
44WebInspector.RemoteObject.fromLocalObject = function(value)
45{
46    return new WebInspector.LocalJSONObject(value);
47}
48
49WebInspector.RemoteObject.resolveNode = function(node, callback)
50{
51    function mycallback(error, object)
52    {
53        if (!callback)
54            return;
55
56        if (error || !object)
57            callback(null);
58        else
59            callback(WebInspector.RemoteObject.fromPayload(object));
60    }
61    DOMAgent.resolveNode(node.id, mycallback);
62}
63
64WebInspector.RemoteObject.fromPayload = function(payload)
65{
66    if (typeof payload === "object")
67        return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.description, payload.hasChildren);
68    // FIXME: make sure we only get here with real payloads in the new DebuggerAgent.js.
69    return payload;
70}
71
72WebInspector.RemoteObject.type = function(remoteObject)
73{
74    if (remoteObject === null)
75        return "null";
76
77    var type = typeof remoteObject;
78    if (type !== "object" && type !== "function")
79        return type;
80
81    return remoteObject.type;
82}
83
84WebInspector.RemoteObject.prototype = {
85    get objectId()
86    {
87        return this._objectId;
88    },
89
90    get type()
91    {
92        return this._type;
93    },
94
95    get description()
96    {
97        return this._description;
98    },
99
100    get hasChildren()
101    {
102        return this._hasChildren;
103    },
104
105    isError: function()
106    {
107        return this._type === "error";
108    },
109
110    getOwnProperties: function(callback)
111    {
112        this._getProperties(false, callback);
113    },
114
115    getAllProperties: function(callback)
116    {
117        this._getProperties(true, callback);
118    },
119
120    _getProperties: function(ignoreHasOwnProperty, callback)
121    {
122        if (!this._objectId) {
123            callback([]);
124            return;
125        }
126        function remoteObjectBinder(error, properties)
127        {
128            if (error) {
129                callback(null);
130                return;
131            }
132            for (var i = 0; properties && i < properties.length; ++i)
133                properties[i].value = WebInspector.RemoteObject.fromPayload(properties[i].value);
134            callback(properties);
135        }
136        RuntimeAgent.getProperties(this._objectId, !!ignoreHasOwnProperty, remoteObjectBinder);
137    },
138
139    setPropertyValue: function(name, value, callback)
140    {
141        if (!this._objectId) {
142            callback("Can't get a property of non-object.");
143            return;
144        }
145        RuntimeAgent.setPropertyValue(this._objectId, name, value, callback);
146    },
147
148    pushNodeToFrontend: function(callback)
149    {
150        if (this._objectId)
151            WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback);
152        else
153            callback(0);
154    },
155
156    evaluate: function(expression, callback)
157    {
158        RuntimeAgent.evaluateOn(this._objectId, expression, callback);
159    },
160
161    release: function()
162    {
163        RuntimeAgent.releaseObject(this._objectId);
164    }
165}
166
167WebInspector.RemoteObjectProperty = function(name, value)
168{
169    this.name = name;
170    this.value = value;
171}
172
173// The below is a wrapper around a local object that provides an interface comaptible
174// with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection).
175// Note that only JSON-compliant objects are currently supported, as there's no provision
176// for traversing prototypes, extracting class names via constuctor, handling properties
177// or functions.
178
179WebInspector.LocalJSONObject = function(value)
180{
181    this._value = value;
182}
183
184WebInspector.LocalJSONObject.prototype = {
185    get description()
186    {
187        if (this._cachedDescription)
188            return this._cachedDescription;
189
190        var type = this.type;
191
192        switch (type) {
193            case "array":
194                function formatArrayItem(property)
195                {
196                    return property.value.description;
197                }
198                this._cachedDescription = this._concatenate("[", "]", formatArrayItem);
199                break;
200            case "object":
201                function formatObjectItem(property)
202                {
203                    return property.name + ":" + property.value.description;
204                }
205                this._cachedDescription = this._concatenate("{", "}", formatObjectItem);
206                break;
207            default:
208                this._cachedDescription = String(this._value);
209        }
210        return this._cachedDescription;
211    },
212
213    _concatenate: function(prefix, suffix, formatProperty)
214    {
215        const previewChars = 100;
216
217        var buffer = prefix;
218        var children = this._children();
219        for (var i = 0; i < children.length; ++i) {
220            var itemDescription = formatProperty(children[i]);
221            if (buffer.length + itemDescription.length > previewChars) {
222                buffer += ",\u2026";
223                break;
224            }
225            if (i)
226                buffer += ", ";
227            buffer += itemDescription;
228        }
229        buffer += suffix;
230        return buffer;
231    },
232
233    get type()
234    {
235        if (this._value === null)
236            return "null";
237        if (this._value instanceof Array)
238            return "array";
239        return typeof this._value;
240    },
241
242    get hasChildren()
243    {
244        return typeof this._value === "object" && this._value !== null && Object.keys(this._value).length;
245    },
246
247    getOwnProperties: function(callback)
248    {
249        callback(this._children());
250    },
251
252    getAllProperties: function(callback)
253    {
254        callback(this._children());
255    },
256
257    _children: function()
258    {
259        function buildProperty(propName)
260        {
261            return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName]));
262        }
263        if (!this._cachedChildren)
264            this._cachedChildren = Object.keys(this._value).map(buildProperty.bind(this));
265        return this._cachedChildren;
266    },
267
268    isError: function()
269    {
270        return false;
271    }
272}
273