1/*
2 * Copyright (C) 2013 Apple 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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "IDBTransactionBackendOperations.h"
28
29#include "IDBCursorBackend.h"
30#include "IDBDatabaseCallbacks.h"
31#include "IDBKeyRange.h"
32#include "IDBRecordIdentifier.h"
33#include "IDBServerConnection.h"
34#include "Logging.h"
35#include <wtf/text/CString.h>
36
37#if ENABLE(INDEXED_DATABASE)
38
39#define STANDARD_DATABASE_ERROR_CALLBACK std::function<void(PassRefPtr<IDBDatabaseError>)> operationCallback = \
40    [operation, completionCallback](PassRefPtr<IDBDatabaseError> error) { \
41        if (error) \
42            operation->m_transaction->abort(error); \
43        completionCallback(); \
44    };
45
46namespace WebCore {
47
48void CreateObjectStoreOperation::perform(std::function<void()> completionCallback)
49{
50    LOG(StorageAPI, "CreateObjectStoreOperation");
51
52    RefPtr<CreateObjectStoreOperation> operation(this);
53    STANDARD_DATABASE_ERROR_CALLBACK;
54
55    m_transaction->database().serverConnection().createObjectStore(*m_transaction, *this, operationCallback);
56}
57
58void CreateIndexOperation::perform(std::function<void()> completionCallback)
59{
60    LOG(StorageAPI, "CreateIndexOperation");
61
62    RefPtr<CreateIndexOperation> operation(this);
63    STANDARD_DATABASE_ERROR_CALLBACK;
64
65    m_transaction->database().serverConnection().createIndex(*m_transaction, *this, operationCallback);
66}
67
68void CreateIndexAbortOperation::perform()
69{
70    LOG(StorageAPI, "CreateIndexAbortOperation");
71    m_transaction->database().removeIndex(m_objectStoreID, m_indexID);
72}
73
74void DeleteIndexOperation::perform(std::function<void()> completionCallback)
75{
76    LOG(StorageAPI, "DeleteIndexOperation");
77
78    RefPtr<DeleteIndexOperation> operation(this);
79    STANDARD_DATABASE_ERROR_CALLBACK;
80
81    m_transaction->database().serverConnection().deleteIndex(*m_transaction, *this, operationCallback);
82}
83
84void DeleteIndexAbortOperation::perform()
85{
86    LOG(StorageAPI, "DeleteIndexAbortOperation");
87    m_transaction->database().addIndex(m_objectStoreID, m_indexMetadata, IDBIndexMetadata::InvalidId);
88}
89
90void GetOperation::perform(std::function<void()> completionCallback)
91{
92    LOG(StorageAPI, "GetOperation");
93
94    RefPtr<GetOperation> operation(this);
95    STANDARD_DATABASE_ERROR_CALLBACK;
96
97    m_transaction->database().serverConnection().get(*m_transaction, *this, [this, operation, operationCallback](const IDBGetResult& result, PassRefPtr<IDBDatabaseError> prpError) {
98        RefPtr<IDBDatabaseError> error = prpError;
99
100        if (error)
101            m_callbacks->onError(error);
102        else {
103            if (!result.valueBuffer) {
104                if (result.keyData.isNull)
105                    m_callbacks->onSuccess();
106                else
107                    m_callbacks->onSuccess(result.keyData.maybeCreateIDBKey());
108            } else {
109                if (!result.keyData.isNull)
110                    m_callbacks->onSuccess(result.valueBuffer, result.keyData.maybeCreateIDBKey(), result.keyPath);
111                else
112                    m_callbacks->onSuccess(result.valueBuffer.get());
113            }
114        }
115
116        operationCallback(error.release());
117    });
118}
119
120void PutOperation::perform(std::function<void()> completionCallback)
121{
122    LOG(StorageAPI, "PutOperation");
123    ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
124    ASSERT(m_indexIDs.size() == m_indexKeys.size());
125
126    m_transaction->database().serverConnection().put(*m_transaction, *this, [this, completionCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBDatabaseError> prpError) {
127        RefPtr<IDBDatabaseError> error = prpError;
128        if (key) {
129            ASSERT(!error);
130            m_callbacks->onSuccess(key);
131        } else {
132            ASSERT(error);
133            m_callbacks->onError(error);
134        }
135        completionCallback();
136    });
137}
138
139void SetIndexesReadyOperation::perform(std::function<void()> completionCallback)
140{
141    LOG(StorageAPI, "SetIndexesReadyOperation");
142
143    for (size_t i = 0; i < m_indexCount; ++i)
144        m_transaction->didCompletePreemptiveEvent();
145
146    callOnMainThread(completionCallback);
147}
148
149void OpenCursorOperation::perform(std::function<void()> completionCallback)
150{
151    LOG(StorageAPI, "OpenCursorOperation");
152
153    RefPtr<OpenCursorOperation> operation(this);
154    auto callback = [this, operation, completionCallback](int64_t cursorID, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBDatabaseError> error) {
155        if (error) {
156            m_callbacks->onError(error);
157        } else if (!key) {
158            // If there's no error but also no key, then the cursor had no records.
159            m_callbacks->onSuccess(static_cast<SharedBuffer*>(0));
160        } else {
161            RefPtr<IDBCursorBackend> cursor = IDBCursorBackend::create(cursorID, m_cursorType, m_taskType, *m_transaction, m_objectStoreID);
162            cursor->updateCursorData(key.get(), primaryKey.get(), valueBuffer.get());
163            m_callbacks->onSuccess(cursor.release());
164        }
165
166        completionCallback();
167    };
168
169    m_transaction->database().serverConnection().openCursor(*m_transaction, *this, callback);
170}
171
172void CountOperation::perform(std::function<void()> completionCallback)
173{
174    LOG(StorageAPI, "CountOperation");
175
176    RefPtr<CountOperation> operation(this);
177    auto callback = [this, operation, completionCallback](int64_t count, PassRefPtr<IDBDatabaseError>) {
178        // FIXME: The LevelDB port never had an error condition for the count operation.
179        // We probably need to support an error for the count operation, breaking the LevelDB port.
180        m_callbacks->onSuccess(count);
181
182        completionCallback();
183    };
184
185    m_transaction->database().serverConnection().count(*m_transaction, *this, callback);
186}
187
188void DeleteRangeOperation::perform(std::function<void()> completionCallback)
189{
190    LOG(StorageAPI, "DeleteRangeOperation");
191
192    RefPtr<DeleteRangeOperation> operation(this);
193    auto callback = [this, operation, completionCallback](PassRefPtr<IDBDatabaseError> error) {
194        if (error)
195            m_callbacks->onError(error);
196        else
197            m_callbacks->onSuccess();
198
199        completionCallback();
200    };
201
202    m_transaction->database().serverConnection().deleteRange(*m_transaction, *this, callback);
203}
204
205void ClearObjectStoreOperation::perform(std::function<void()> completionCallback)
206{
207    LOG(StorageAPI, "ClearObjectStoreOperation");
208
209    RefPtr<ClearObjectStoreOperation> operation(this);
210
211    auto clearCallback = [this, operation, completionCallback](PassRefPtr<IDBDatabaseError> prpError) {
212        RefPtr<IDBDatabaseError> error = prpError;
213
214        if (error) {
215            m_callbacks->onError(error);
216            m_transaction->abort(error.release());
217        } else
218            m_callbacks->onSuccess();
219
220        completionCallback();
221    };
222
223    m_transaction->database().serverConnection().clearObjectStore(*m_transaction, *this, clearCallback);
224}
225
226void DeleteObjectStoreOperation::perform(std::function<void()> completionCallback)
227{
228    LOG(StorageAPI, "DeleteObjectStoreOperation");
229
230    RefPtr<DeleteObjectStoreOperation> operation(this);
231    STANDARD_DATABASE_ERROR_CALLBACK;
232
233    m_transaction->database().serverConnection().deleteObjectStore(*m_transaction, *this, operationCallback);
234}
235
236void IDBDatabaseBackend::VersionChangeOperation::perform(std::function<void()> completionCallback)
237{
238    LOG(StorageAPI, "VersionChangeOperation");
239
240    uint64_t oldVersion = m_transaction->database().metadata().version;
241    RefPtr<IDBDatabaseBackend::VersionChangeOperation> operation(this);
242    ASSERT(static_cast<uint64_t>(m_version) > oldVersion || oldVersion == IDBDatabaseMetadata::NoIntVersion);
243
244    std::function<void(PassRefPtr<IDBDatabaseError>)> operationCallback = [oldVersion, operation, this, completionCallback](PassRefPtr<IDBDatabaseError> prpError) {
245        RefPtr<IDBDatabaseError> error = prpError;
246        if (error) {
247            m_callbacks->onError(error);
248            m_transaction->abort(error);
249        } else {
250            ASSERT(!m_transaction->database().hasPendingSecondHalfOpen());
251            m_transaction->database().setCurrentVersion(m_version);
252            m_transaction->database().setPendingSecondHalfOpen(std::make_unique<IDBPendingOpenCall>(*m_callbacks, *m_databaseCallbacks, m_transaction->id(), m_version));
253            m_callbacks->onUpgradeNeeded(oldVersion, &m_transaction->database(), m_transaction->database().metadata());
254        }
255        completionCallback();
256    };
257
258    m_transaction->database().serverConnection().changeDatabaseVersion(*m_transaction, *this, operationCallback);
259}
260
261void CreateObjectStoreAbortOperation::perform()
262{
263    LOG(StorageAPI, "CreateObjectStoreAbortOperation");
264    m_transaction->database().removeObjectStore(m_objectStoreID);
265}
266
267void DeleteObjectStoreAbortOperation::perform()
268{
269    LOG(StorageAPI, "DeleteObjectStoreAbortOperation");
270    m_transaction->database().addObjectStore(m_objectStoreMetadata, IDBObjectStoreMetadata::InvalidId);
271}
272
273void IDBDatabaseBackend::VersionChangeAbortOperation::perform()
274{
275    LOG(StorageAPI, "VersionChangeAbortOperation");
276    m_transaction->database().setCurrentVersion(m_previousIntVersion);
277}
278
279} // namespace WebCore
280
281#endif // ENABLE(INDEXED_DATABASE)
282