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 "modules/indexeddb/IDBIndex.h"
28
29#include "bindings/core/v8/ExceptionState.h"
30#include "bindings/modules/v8/IDBBindingUtilities.h"
31#include "core/dom/ExceptionCode.h"
32#include "core/dom/ExecutionContext.h"
33#include "modules/indexeddb/IDBDatabase.h"
34#include "modules/indexeddb/IDBKey.h"
35#include "modules/indexeddb/IDBObjectStore.h"
36#include "modules/indexeddb/IDBTracing.h"
37#include "modules/indexeddb/IDBTransaction.h"
38#include "modules/indexeddb/WebIDBCallbacksImpl.h"
39#include "public/platform/WebIDBKeyRange.h"
40
41using blink::WebIDBCallbacks;
42using blink::WebIDBCursor;
43using blink::WebIDBDatabase;
44
45namespace blink {
46
47IDBIndex::IDBIndex(const IDBIndexMetadata& metadata, IDBObjectStore* objectStore, IDBTransaction* transaction)
48    : m_metadata(metadata)
49    , m_objectStore(objectStore)
50    , m_transaction(transaction)
51    , m_deleted(false)
52{
53    ASSERT(m_objectStore);
54    ASSERT(m_transaction);
55    ASSERT(m_metadata.id != IDBIndexMetadata::InvalidId);
56}
57
58IDBIndex::~IDBIndex()
59{
60}
61
62void IDBIndex::trace(Visitor* visitor)
63{
64    visitor->trace(m_objectStore);
65    visitor->trace(m_transaction);
66}
67
68ScriptValue IDBIndex::keyPath(ScriptState* scriptState) const
69{
70    return idbAnyToScriptValue(scriptState, IDBAny::create(m_metadata.keyPath));
71}
72
73IDBRequest* IDBIndex::openCursor(ScriptState* scriptState, const ScriptValue& range, const String& directionString, ExceptionState& exceptionState)
74{
75    IDB_TRACE("IDBIndex::openCursor");
76    if (isDeleted()) {
77        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::indexDeletedErrorMessage);
78        return 0;
79    }
80    if (m_transaction->isFinished() || m_transaction->isFinishing()) {
81        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
82        return 0;
83    }
84    if (!m_transaction->isActive()) {
85        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
86        return 0;
87    }
88    WebIDBCursorDirection direction = IDBCursor::stringToDirection(directionString, exceptionState);
89    if (exceptionState.hadException())
90        return 0;
91
92    IDBKeyRange* keyRange = IDBKeyRange::fromScriptValue(scriptState->executionContext(), range, exceptionState);
93    if (exceptionState.hadException())
94        return 0;
95
96    if (!backendDB()) {
97        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
98        return 0;
99    }
100
101    return openCursor(scriptState, keyRange, direction);
102}
103
104IDBRequest* IDBIndex::openCursor(ScriptState* scriptState, IDBKeyRange* keyRange, WebIDBCursorDirection direction)
105{
106    IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
107    request->setCursorDetails(IndexedDB::CursorKeyAndValue, direction);
108    backendDB()->openCursor(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, direction, false, WebIDBTaskTypeNormal, WebIDBCallbacksImpl::create(request).leakPtr());
109    return request;
110}
111
112IDBRequest* IDBIndex::count(ScriptState* scriptState, const ScriptValue& range, ExceptionState& exceptionState)
113{
114    IDB_TRACE("IDBIndex::count");
115    if (isDeleted()) {
116        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::indexDeletedErrorMessage);
117        return 0;
118    }
119    if (m_transaction->isFinished() || m_transaction->isFinishing()) {
120        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
121        return 0;
122    }
123    if (!m_transaction->isActive()) {
124        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
125        return 0;
126    }
127
128    IDBKeyRange* keyRange = IDBKeyRange::fromScriptValue(scriptState->executionContext(), range, exceptionState);
129    if (exceptionState.hadException())
130        return 0;
131
132    if (!backendDB()) {
133        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
134        return 0;
135    }
136
137    IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
138    backendDB()->count(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, WebIDBCallbacksImpl::create(request).leakPtr());
139    return request;
140}
141
142IDBRequest* IDBIndex::openKeyCursor(ScriptState* scriptState, const ScriptValue& range, const String& directionString, ExceptionState& exceptionState)
143{
144    IDB_TRACE("IDBIndex::openKeyCursor");
145    if (isDeleted()) {
146        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::indexDeletedErrorMessage);
147        return 0;
148    }
149    if (m_transaction->isFinished() || m_transaction->isFinishing()) {
150        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
151        return 0;
152    }
153    if (!m_transaction->isActive()) {
154        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
155        return 0;
156    }
157    WebIDBCursorDirection direction = IDBCursor::stringToDirection(directionString, exceptionState);
158    if (exceptionState.hadException())
159        return 0;
160
161    IDBKeyRange* keyRange = IDBKeyRange::fromScriptValue(scriptState->executionContext(), range, exceptionState);
162    if (exceptionState.hadException())
163        return 0;
164    if (!backendDB()) {
165        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
166        return 0;
167    }
168
169    IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
170    request->setCursorDetails(IndexedDB::CursorKeyOnly, direction);
171    backendDB()->openCursor(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, direction, true, WebIDBTaskTypeNormal, WebIDBCallbacksImpl::create(request).leakPtr());
172    return request;
173}
174
175IDBRequest* IDBIndex::get(ScriptState* scriptState, const ScriptValue& key, ExceptionState& exceptionState)
176{
177    IDB_TRACE("IDBIndex::get");
178    return getInternal(scriptState, key, exceptionState, false);
179}
180
181IDBRequest* IDBIndex::getKey(ScriptState* scriptState, const ScriptValue& key, ExceptionState& exceptionState)
182{
183    IDB_TRACE("IDBIndex::getKey");
184    return getInternal(scriptState, key, exceptionState, true);
185}
186
187IDBRequest* IDBIndex::getInternal(ScriptState* scriptState, const ScriptValue& key, ExceptionState& exceptionState, bool keyOnly)
188{
189    if (isDeleted()) {
190        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::indexDeletedErrorMessage);
191        return 0;
192    }
193    if (m_transaction->isFinished() || m_transaction->isFinishing()) {
194        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
195        return 0;
196    }
197    if (!m_transaction->isActive()) {
198        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
199        return 0;
200    }
201
202    IDBKeyRange* keyRange = IDBKeyRange::fromScriptValue(scriptState->executionContext(), key, exceptionState);
203    if (exceptionState.hadException())
204        return 0;
205    if (!keyRange) {
206        exceptionState.throwDOMException(DataError, IDBDatabase::noKeyOrKeyRangeErrorMessage);
207        return 0;
208    }
209    if (!backendDB()) {
210        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
211        return 0;
212    }
213
214    IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
215    backendDB()->get(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, keyOnly, WebIDBCallbacksImpl::create(request).leakPtr());
216    return request;
217}
218
219WebIDBDatabase* IDBIndex::backendDB() const
220{
221    return m_transaction->backendDB();
222}
223
224bool IDBIndex::isDeleted() const
225{
226    return m_deleted || m_objectStore->isDeleted();
227}
228
229} // namespace blink
230