IndexedDB: Support Array-type key paths
[WebKit-https.git] / Source / WebCore / bindings / v8 / IDBBindingUtilities.cpp
1 /*
2  * Copyright (C) 2011 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
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBBindingUtilities.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBDatabaseException.h"
32 #include "IDBKey.h"
33 #include "IDBKeyPath.h"
34 #include "IDBTracing.h"
35 #include "SerializedScriptValue.h"
36 #include "V8Binding.h"
37 #include "V8IDBKey.h"
38 #include <wtf/MathExtras.h>
39 #include <wtf/Vector.h>
40
41 namespace WebCore {
42
43 static const size_t maximumDepth = 2000;
44
45 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, Vector<v8::Handle<v8::Array> >& stack)
46 {
47     if (value->IsNumber() && !isnan(value->NumberValue()))
48         return IDBKey::createNumber(value->NumberValue());
49     if (value->IsString())
50         return IDBKey::createString(v8ValueToWebCoreString(value));
51     if (value->IsDate() && !isnan(value->NumberValue()))
52         return IDBKey::createDate(value->NumberValue());
53     if (value->IsArray()) {
54         v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
55
56         if (stack.contains(array))
57             return 0;
58         if (stack.size() >= maximumDepth)
59             return 0;
60         stack.append(array);
61
62         IDBKey::KeyArray subkeys;
63         uint32_t length = array->Length();
64         for (uint32_t i = 0; i < length; ++i) {
65             v8::Local<v8::Value> item = array->Get(v8::Int32::New(i));
66             RefPtr<IDBKey> subkey = createIDBKeyFromValue(item, stack);
67             if (!subkey)
68                 return 0;
69             subkeys.append(subkey);
70         }
71
72         stack.removeLast();
73         return IDBKey::createArray(subkeys);
74     }
75     return 0;
76 }
77
78 PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value)
79 {
80     Vector<v8::Handle<v8::Array> > stack;
81     RefPtr<IDBKey> key = createIDBKeyFromValue(value, stack);
82     if (key)
83         return key;
84     return IDBKey::createInvalid();
85 }
86
87 namespace {
88
89 template<typename T>
90 bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value)
91 {
92     v8::Local<v8::Object> object = v8Value->ToObject();
93     if (!object->Has(indexOrName))
94         return false;
95     v8Value = object->Get(indexOrName);
96     return true;
97 }
98
99 template<typename T>
100 bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value)
101 {
102     v8::Local<v8::Object> object = v8Object->ToObject();
103     ASSERT(!object->Has(indexOrName));
104     return object->Set(indexOrName, v8Value);
105 }
106
107 bool get(v8::Handle<v8::Value>& object, const String& keyPathElement)
108 {
109     if (object->IsString() && keyPathElement == "length") {
110         int32_t length = v8::Handle<v8::String>::Cast(object)->Length();
111         object = v8::Number::New(length);
112         return true;
113     }
114     return object->IsObject() && getValueFrom(v8String(keyPathElement), object);
115 }
116
117 bool set(v8::Handle<v8::Value>& object, const String& keyPathElement, const v8::Handle<v8::Value>& v8Value)
118 {
119     return object->IsObject() && setValue(object, v8String(keyPathElement), v8Value);
120 }
121
122 v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index)
123 {
124     v8::Handle<v8::Value> currentValue(rootValue);
125
126     ASSERT(index <= keyPathElements.size());
127     for (size_t i = 0; i < index; ++i) {
128         if (!get(currentValue, keyPathElements[i]))
129             return v8::Handle<v8::Value>();
130     }
131
132     return currentValue;
133 }
134
135 v8::Handle<v8::Value> ensureNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index)
136 {
137     v8::Handle<v8::Value> currentValue(rootValue);
138
139     ASSERT(index <= keyPathElements.size());
140     for (size_t i = 0; i < index; ++i) {
141         v8::Handle<v8::Value> parentValue(currentValue);
142         const String& keyPathElement = keyPathElements[i];
143         if (!get(currentValue, keyPathElement)) {
144             v8::Handle<v8::Object> object = v8::Object::New();
145             if (!set(parentValue, keyPathElement, object))
146                 return v8::Handle<v8::Value>();
147             currentValue = object;
148         }
149     }
150
151     return currentValue;
152 }
153
154 } // anonymous namespace
155
156 static PassRefPtr<IDBKey> createIDBKeyFromSerializedValueAndKeyPath(PassRefPtr<SerializedScriptValue> value, const String& keyPath)
157 {
158     Vector<String> keyPathElements;
159     IDBKeyPathParseError error;
160     IDBParseKeyPath(keyPath, keyPathElements, error);
161     ASSERT(error == IDBKeyPathParseErrorNone);
162
163     V8AuxiliaryContext context;
164     v8::Handle<v8::Value> v8Value(value->deserialize());
165     v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size()));
166     if (v8Key.IsEmpty())
167         return 0;
168     return createIDBKeyFromValue(v8Key);
169 }
170
171
172 PassRefPtr<IDBKey> createIDBKeyFromSerializedValueAndKeyPath(PassRefPtr<SerializedScriptValue> prpValue, const IDBKeyPath& keyPath)
173 {
174     IDB_TRACE("createIDBKeyFromSerializedValueAndKeyPath");
175     ASSERT(!keyPath.isNull());
176
177     RefPtr<SerializedScriptValue> value = prpValue;
178
179     if (keyPath.type() == IDBKeyPath::ArrayType) {
180         IDBKey::KeyArray result;
181         const Vector<String>& array = keyPath.array();
182         for (size_t i = 0; i < array.size(); ++i) {
183             RefPtr<IDBKey> key = createIDBKeyFromSerializedValueAndKeyPath(value, array[i]);
184             if (!key)
185                 return 0;
186             result.append(key);
187         }
188         return IDBKey::createArray(result);
189     }
190
191     ASSERT(keyPath.type() == IDBKeyPath::StringType);
192     return createIDBKeyFromSerializedValueAndKeyPath(value, keyPath.string());
193 }
194
195 PassRefPtr<SerializedScriptValue> injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const IDBKeyPath& keyPath)
196 {
197     IDB_TRACE("injectIDBKeyIntoSerializedValue");
198
199     ASSERT(keyPath.type() == IDBKeyPath::StringType);
200     Vector<String> keyPathElements;
201     IDBKeyPathParseError error;
202     IDBParseKeyPath(keyPath.string(), keyPathElements, error);
203     ASSERT(error == IDBKeyPathParseErrorNone);
204
205     if (!keyPathElements.size())
206         return 0;
207
208     V8AuxiliaryContext context;
209     v8::Handle<v8::Value> v8Value(value->deserialize());
210     v8::Handle<v8::Value> parent(ensureNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size() - 1));
211     if (parent.IsEmpty())
212         return 0;
213
214     if (!set(parent, keyPathElements.last(), toV8(key.get())))
215         return 0;
216
217     return SerializedScriptValue::create(v8Value);
218 }
219
220 } // namespace WebCore
221
222 #endif