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
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 "SerializedScriptValue.h"
35#include "V8Binding.h"
36#include "V8IDBKey.h"
37#include <wtf/Vector.h>
38
39namespace WebCore {
40
41PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value)
42{
43    if (value->IsNull())
44        return IDBKey::createNull();
45    if (value->IsNumber())
46        return IDBKey::createNumber(value->NumberValue());
47    if (value->IsString())
48        return IDBKey::createString(v8ValueToWebCoreString(value));
49    if (value->IsDate())
50        return IDBKey::createDate(value->NumberValue());
51
52    return 0; // Signals type error.
53}
54
55namespace {
56
57template<typename T>
58bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value)
59{
60    v8::Local<v8::Object> object = v8Value->ToObject();
61    if (!object->Has(indexOrName))
62        return false;
63    v8Value = object->Get(indexOrName);
64    return true;
65}
66
67template<typename T>
68bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value)
69{
70    v8::Local<v8::Object> object = v8Object->ToObject();
71    ASSERT(!object->Has(indexOrName));
72    return object->Set(indexOrName, v8Value);
73}
74
75bool get(v8::Handle<v8::Value>& object, const IDBKeyPathElement& keyPathElement)
76{
77    switch (keyPathElement.type) {
78    case IDBKeyPathElement::IsIndexed:
79        return object->IsArray() && getValueFrom(keyPathElement.index, object);
80    case IDBKeyPathElement::IsNamed:
81        return object->IsObject() && getValueFrom(v8String(keyPathElement.identifier), object);
82    default:
83        ASSERT_NOT_REACHED();
84    }
85    return false;
86}
87
88bool set(v8::Handle<v8::Value>& object, const IDBKeyPathElement& keyPathElement, const v8::Handle<v8::Value>& v8Value)
89{
90    switch (keyPathElement.type) {
91    case IDBKeyPathElement::IsIndexed:
92        return object->IsArray() && setValue(object, keyPathElement.index, v8Value);
93    case IDBKeyPathElement::IsNamed:
94        return object->IsObject() && setValue(object, v8String(keyPathElement.identifier), v8Value);
95    default:
96        ASSERT_NOT_REACHED();
97    }
98    return false;
99}
100
101class LocalContext {
102public:
103    LocalContext()
104        : m_context(v8::Context::New())
105    {
106        m_context->Enter();
107    }
108
109    ~LocalContext()
110    {
111        m_context->Exit();
112        m_context.Dispose();
113    }
114
115private:
116    v8::HandleScope m_scope;
117    v8::Persistent<v8::Context> m_context;
118};
119
120v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<IDBKeyPathElement>& keyPathElements, size_t index)
121{
122    v8::Handle<v8::Value> currentValue(rootValue);
123
124    ASSERT(index <= keyPathElements.size());
125    for (size_t i = 0; i < index; ++i) {
126        if (!get(currentValue, keyPathElements[i]))
127            return v8::Handle<v8::Value>();
128    }
129
130    return currentValue;
131}
132
133} // anonymous namespace
134
135PassRefPtr<IDBKey> createIDBKeyFromSerializedValueAndKeyPath(PassRefPtr<SerializedScriptValue> value, const Vector<IDBKeyPathElement>& keyPath)
136{
137    LocalContext localContext;
138    v8::Handle<v8::Value> v8Value(value->deserialize());
139    v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(v8Value, keyPath, keyPath.size()));
140    if (v8Key.IsEmpty())
141        return 0;
142    return createIDBKeyFromValue(v8Key);
143}
144
145PassRefPtr<SerializedScriptValue> injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const Vector<IDBKeyPathElement>& keyPath)
146{
147    LocalContext localContext;
148    if (!keyPath.size())
149        return 0;
150
151    v8::Handle<v8::Value> v8Value(value->deserialize());
152    v8::Handle<v8::Value> parent(getNthValueOnKeyPath(v8Value, keyPath, keyPath.size() - 1));
153    if (parent.IsEmpty())
154        return 0;
155
156    if (!set(parent, keyPath.last(), toV8(key.get())))
157        return 0;
158
159    return SerializedScriptValue::create(v8Value);
160}
161
162} // namespace WebCore
163
164#endif
165