1/*
2 * Copyright (C) 2010 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#include "config.h"
32#include "CppVariant.h"
33
34#include "WebBindings.h"
35#include <limits>
36#include <wtf/Assertions.h>
37#include <wtf/StringExtras.h>
38
39using namespace WebKit;
40using namespace std;
41
42CppVariant::CppVariant()
43{
44    type = NPVariantType_Null;
45}
46
47// Note that Set() performs a deep copy, which is necessary to safely
48// call FreeData() on the value in the destructor.
49CppVariant::CppVariant(const CppVariant& original)
50{
51    type = NPVariantType_Null;
52    set(original);
53}
54
55// See comment for copy constructor, above.
56CppVariant& CppVariant::operator=(const CppVariant& original)
57{
58    if (&original != this)
59        set(original);
60    return *this;
61}
62
63CppVariant::~CppVariant()
64{
65    freeData();
66}
67
68void CppVariant::freeData()
69{
70    WebBindings::releaseVariantValue(this);
71}
72
73bool CppVariant::isEqual(const CppVariant& other) const
74{
75    if (type != other.type)
76        return false;
77
78    switch (type) {
79    case NPVariantType_Bool:
80        return (value.boolValue == other.value.boolValue);
81    case NPVariantType_Int32:
82        return (value.intValue == other.value.intValue);
83    case NPVariantType_Double:
84        return (value.doubleValue == other.value.doubleValue);
85    case NPVariantType_String: {
86        const NPString *this_value = &value.stringValue;
87        const NPString *other_value = &other.value.stringValue;
88        uint32_t len = this_value->UTF8Length;
89        return len == other_value->UTF8Length
90            && !strncmp(this_value->UTF8Characters,
91                        other_value->UTF8Characters, len);
92    }
93    case NPVariantType_Null:
94    case NPVariantType_Void:
95        return true;
96    case NPVariantType_Object: {
97        NPObject* thisValue = value.objectValue;
98        NPObject* otherValue = other.value.objectValue;
99        return thisValue->_class == otherValue->_class
100            && thisValue->referenceCount == otherValue->referenceCount;
101    }
102    }
103    return false;
104}
105
106void CppVariant::copyToNPVariant(NPVariant* result) const
107{
108    result->type = type;
109    switch (type) {
110    case NPVariantType_Bool:
111        result->value.boolValue = value.boolValue;
112        break;
113    case NPVariantType_Int32:
114        result->value.intValue = value.intValue;
115        break;
116    case NPVariantType_Double:
117        result->value.doubleValue = value.doubleValue;
118        break;
119    case NPVariantType_String:
120        WebBindings::initializeVariantWithStringCopy(result, &value.stringValue);
121        break;
122    case NPVariantType_Null:
123    case NPVariantType_Void:
124        // Nothing to set.
125        break;
126    case NPVariantType_Object:
127        result->type = NPVariantType_Object;
128        result->value.objectValue = WebBindings::retainObject(value.objectValue);
129        break;
130    }
131}
132
133void CppVariant::set(const NPVariant& newValue)
134{
135    freeData();
136    switch (newValue.type) {
137    case NPVariantType_Bool:
138        set(newValue.value.boolValue);
139        break;
140    case NPVariantType_Int32:
141        set(newValue.value.intValue);
142        break;
143    case NPVariantType_Double:
144        set(newValue.value.doubleValue);
145        break;
146    case NPVariantType_String:
147        set(newValue.value.stringValue);
148        break;
149    case NPVariantType_Null:
150    case NPVariantType_Void:
151        type = newValue.type;
152        break;
153    case NPVariantType_Object:
154        set(newValue.value.objectValue);
155        break;
156    }
157}
158
159void CppVariant::setNull()
160{
161    freeData();
162    type = NPVariantType_Null;
163}
164
165void CppVariant::set(bool newValue)
166{
167    freeData();
168    type = NPVariantType_Bool;
169    value.boolValue = newValue;
170}
171
172void CppVariant::set(int32_t newValue)
173{
174    freeData();
175    type = NPVariantType_Int32;
176    value.intValue = newValue;
177}
178
179void CppVariant::set(double newValue)
180{
181    freeData();
182    type = NPVariantType_Double;
183    value.doubleValue = newValue;
184}
185
186// The newValue must be a null-terminated string.
187void CppVariant::set(const char* newValue)
188{
189    freeData();
190    type = NPVariantType_String;
191    NPString newString = {newValue,
192                          static_cast<uint32_t>(strlen(newValue))};
193    WebBindings::initializeVariantWithStringCopy(this, &newString);
194}
195
196void CppVariant::set(const string& newValue)
197{
198    freeData();
199    type = NPVariantType_String;
200    NPString newString = {newValue.data(),
201                          static_cast<uint32_t>(newValue.size())};
202    WebBindings::initializeVariantWithStringCopy(this, &newString);
203}
204
205void CppVariant::set(const NPString& newValue)
206{
207    freeData();
208    type = NPVariantType_String;
209    WebBindings::initializeVariantWithStringCopy(this, &newValue);
210}
211
212void CppVariant::set(NPObject* newValue)
213{
214    freeData();
215    type = NPVariantType_Object;
216    value.objectValue = WebBindings::retainObject(newValue);
217}
218
219string CppVariant::toString() const
220{
221    ASSERT(isString());
222    return string(value.stringValue.UTF8Characters,
223                  value.stringValue.UTF8Length);
224}
225
226int32_t CppVariant::toInt32() const
227{
228    if (isInt32())
229        return value.intValue;
230    if (isDouble())
231        return static_cast<int32_t>(value.doubleValue);
232    ASSERT_NOT_REACHED();
233    return 0;
234}
235
236double CppVariant::toDouble() const
237{
238    if (isInt32())
239        return static_cast<double>(value.intValue);
240    if (isDouble())
241        return value.doubleValue;
242    ASSERT_NOT_REACHED();
243    return 0;
244}
245
246bool CppVariant::toBoolean() const
247{
248    ASSERT(isBool());
249    return value.boolValue;
250}
251
252Vector<string> CppVariant::toStringVector() const
253{
254
255    ASSERT(isObject());
256    Vector<string> stringVector;
257    NPObject* npValue = value.objectValue;
258    NPIdentifier lengthId = WebBindings::getStringIdentifier("length");
259
260    if (!WebBindings::hasProperty(0, npValue, lengthId))
261        return stringVector;
262
263    NPVariant lengthValue;
264    if (!WebBindings::getProperty(0, npValue, lengthId, &lengthValue))
265        return stringVector;
266
267    int length = 0;
268    // The length is a double in some cases.
269    if (NPVARIANT_IS_DOUBLE(lengthValue))
270        length = static_cast<int>(NPVARIANT_TO_DOUBLE(lengthValue));
271    else if (NPVARIANT_IS_INT32(lengthValue))
272        length = NPVARIANT_TO_INT32(lengthValue);
273    WebBindings::releaseVariantValue(&lengthValue);
274
275    // For sanity, only allow 100 items.
276    length = min(100, length);
277    for (int i = 0; i < length; ++i) {
278        // Get each of the items.
279        char indexInChar[20]; // Enough size to store 32-bit integer
280        snprintf(indexInChar, 20, "%d", i);
281        string index(indexInChar);
282        NPIdentifier indexId = WebBindings::getStringIdentifier(index.c_str());
283        if (!WebBindings::hasProperty(0, npValue, indexId))
284            continue;
285        NPVariant indexValue;
286        if (!WebBindings::getProperty(0, npValue, indexId, &indexValue))
287            continue;
288        if (NPVARIANT_IS_STRING(indexValue)) {
289            string item(NPVARIANT_TO_STRING(indexValue).UTF8Characters,
290                        NPVARIANT_TO_STRING(indexValue).UTF8Length);
291            stringVector.append(item);
292        }
293        WebBindings::releaseVariantValue(&indexValue);
294    }
295    return stringVector;
296}
297
298bool CppVariant::invoke(const string& method, const CppVariant* arguments,
299                        uint32_t argumentCount, CppVariant& result) const
300{
301    ASSERT(isObject());
302    NPIdentifier methodName = WebBindings::getStringIdentifier(method.c_str());
303    NPObject* npObject = value.objectValue;
304    if (!WebBindings::hasMethod(0, npObject, methodName))
305        return false;
306    NPVariant r;
307    bool status = WebBindings::invoke(0, npObject, methodName, arguments, argumentCount, &r);
308    result.set(r);
309    return status;
310}
311