1/*
2 * Copyright (C) 2016 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 "IDBConnectionProxy.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "IDBCursorInfo.h"
32#include "IDBDatabase.h"
33#include "IDBGetRecordData.h"
34#include "IDBIterateCursorData.h"
35#include "IDBKeyRangeData.h"
36#include "IDBOpenDBRequest.h"
37#include "IDBRequestData.h"
38#include "IDBResultData.h"
39#include "ScriptExecutionContext.h"
40#include "SecurityOrigin.h"
41#include <wtf/MainThread.h>
42
43namespace WebCore {
44namespace IDBClient {
45
46IDBConnectionProxy::IDBConnectionProxy(IDBConnectionToServer& connection)
47 : m_connectionToServer(connection)
48 , m_serverConnectionIdentifier(connection.identifier())
49{
50 ASSERT(isMainThread());
51}
52
53void IDBConnectionProxy::ref()
54{
55 m_connectionToServer.ref();
56}
57
58void IDBConnectionProxy::deref()
59{
60 m_connectionToServer.deref();
61}
62
63Ref<IDBOpenDBRequest> IDBConnectionProxy::openDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version)
64{
65 RefPtr<IDBOpenDBRequest> request;
66 {
67 Locker<Lock> locker(m_openDBRequestMapLock);
68
69 request = IDBOpenDBRequest::createOpenRequest(context, *this, databaseIdentifier, version);
70 ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier()));
71 m_openDBRequestMap.set(request->resourceIdentifier(), request.get());
72 }
73
74 callConnectionOnMainThread(&IDBConnectionToServer::openDatabase, IDBRequestData(*this, *request));
75
76 return request.releaseNonNull();
77}
78
79Ref<IDBOpenDBRequest> IDBConnectionProxy::deleteDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier)
80{
81 RefPtr<IDBOpenDBRequest> request;
82 {
83 Locker<Lock> locker(m_openDBRequestMapLock);
84
85 request = IDBOpenDBRequest::createDeleteRequest(context, *this, databaseIdentifier);
86 ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier()));
87 m_openDBRequestMap.set(request->resourceIdentifier(), request.get());
88 }
89
90 callConnectionOnMainThread(&IDBConnectionToServer::deleteDatabase, IDBRequestData(*this, *request));
91
92 return request.releaseNonNull();
93}
94
95void IDBConnectionProxy::didOpenDatabase(const IDBResultData& resultData)
96{
97 completeOpenDBRequest(resultData);
98}
99
100void IDBConnectionProxy::didDeleteDatabase(const IDBResultData& resultData)
101{
102 completeOpenDBRequest(resultData);
103}
104
105void IDBConnectionProxy::completeOpenDBRequest(const IDBResultData& resultData)
106{
107 ASSERT(isMainThread());
108
109 RefPtr<IDBOpenDBRequest> request;
110 {
111 Locker<Lock> locker(m_openDBRequestMapLock);
112 request = m_openDBRequestMap.take(resultData.requestIdentifier());
113 }
114
115 if (!request)
116 return;
117
118 request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, resultData);
119}
120
121void IDBConnectionProxy::createObjectStore(TransactionOperation& operation, const IDBObjectStoreInfo& info)
122{
123 const IDBRequestData requestData { operation };
124 saveOperation(operation);
125
126 callConnectionOnMainThread(&IDBConnectionToServer::createObjectStore, requestData, info);
127}
128
129void IDBConnectionProxy::renameObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& newName)
130{
131 const IDBRequestData requestData { operation };
132 saveOperation(operation);
133
134 callConnectionOnMainThread(&IDBConnectionToServer::renameObjectStore, requestData, objectStoreIdentifier, newName);
135}
136
137void IDBConnectionProxy::renameIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
138{
139 const IDBRequestData requestData { operation };
140 saveOperation(operation);
141
142 callConnectionOnMainThread(&IDBConnectionToServer::renameIndex, requestData, objectStoreIdentifier, indexIdentifier, newName);
143}
144
145void IDBConnectionProxy::deleteObjectStore(TransactionOperation& operation, const String& objectStoreName)
146{
147 const IDBRequestData requestData { operation };
148 saveOperation(operation);
149
150 callConnectionOnMainThread(&IDBConnectionToServer::deleteObjectStore, requestData, objectStoreName);
151}
152
153void IDBConnectionProxy::clearObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier)
154{
155 const IDBRequestData requestData { operation };
156 saveOperation(operation);
157
158 callConnectionOnMainThread(&IDBConnectionToServer::clearObjectStore, requestData, objectStoreIdentifier);
159}
160
161void IDBConnectionProxy::createIndex(TransactionOperation& operation, const IDBIndexInfo& info)
162{
163 const IDBRequestData requestData { operation };
164 saveOperation(operation);
165
166 callConnectionOnMainThread(&IDBConnectionToServer::createIndex, requestData, info);
167}
168
169void IDBConnectionProxy::deleteIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& indexName)
170{
171 const IDBRequestData requestData { operation };
172 saveOperation(operation);
173
174 callConnectionOnMainThread(&IDBConnectionToServer::deleteIndex, requestData, WTFMove(objectStoreIdentifier), indexName);
175}
176
177void IDBConnectionProxy::putOrAdd(TransactionOperation& operation, IDBKeyData&& keyData, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode mode)
178{
179 const IDBRequestData requestData { operation };
180 saveOperation(operation);
181
182 callConnectionOnMainThread(&IDBConnectionToServer::putOrAdd, requestData, keyData, value, mode);
183}
184
185void IDBConnectionProxy::getRecord(TransactionOperation& operation, const IDBGetRecordData& getRecordData)
186{
187 const IDBRequestData requestData { operation };
188 saveOperation(operation);
189
190 callConnectionOnMainThread(&IDBConnectionToServer::getRecord, requestData, getRecordData);
191}
192
193void IDBConnectionProxy::getAllRecords(TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData)
194{
195 const IDBRequestData requestData { operation };
196 saveOperation(operation);
197
198 callConnectionOnMainThread(&IDBConnectionToServer::getAllRecords, requestData, getAllRecordsData);
199}
200
201void IDBConnectionProxy::getCount(TransactionOperation& operation, const IDBKeyRangeData& keyRange)
202{
203 const IDBRequestData requestData { operation };
204 saveOperation(operation);
205
206 callConnectionOnMainThread(&IDBConnectionToServer::getCount, requestData, keyRange);
207}
208
209void IDBConnectionProxy::deleteRecord(TransactionOperation& operation, const IDBKeyRangeData& keyRange)
210{
211 const IDBRequestData requestData { operation };
212 saveOperation(operation);
213
214 callConnectionOnMainThread(&IDBConnectionToServer::deleteRecord, requestData, keyRange);
215}
216
217void IDBConnectionProxy::openCursor(TransactionOperation& operation, const IDBCursorInfo& info)
218{
219 const IDBRequestData requestData { operation };
220 saveOperation(operation);
221
222 callConnectionOnMainThread(&IDBConnectionToServer::openCursor, requestData, info);
223}
224
225void IDBConnectionProxy::iterateCursor(TransactionOperation& operation, const IDBIterateCursorData& data)
226{
227 const IDBRequestData requestData { operation };
228 saveOperation(operation);
229
230 callConnectionOnMainThread(&IDBConnectionToServer::iterateCursor, requestData, data);
231}
232
233void IDBConnectionProxy::saveOperation(TransactionOperation& operation)
234{
235 Locker<Lock> locker(m_transactionOperationLock);
236
237 ASSERT(!m_activeOperations.contains(operation.identifier()));
238 m_activeOperations.set(operation.identifier(), &operation);
239}
240
241void IDBConnectionProxy::completeOperation(const IDBResultData& resultData)
242{
243 RefPtr<TransactionOperation> operation;
244 {
245 Locker<Lock> locker(m_transactionOperationLock);
246 operation = m_activeOperations.take(resultData.requestIdentifier());
247 }
248
249 if (!operation)
250 return;
251
252 operation->transitionToComplete(resultData, WTFMove(operation));
253}
254
255void IDBConnectionProxy::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
256{
257 callConnectionOnMainThread(&IDBConnectionToServer::abortOpenAndUpgradeNeeded, databaseConnectionIdentifier, transactionIdentifier);
258}
259
260void IDBConnectionProxy::fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
261{
262 RefPtr<IDBDatabase> database;
263 {
264 Locker<Lock> locker(m_databaseConnectionMapLock);
265 database = m_databaseConnectionMap.get(databaseConnectionIdentifier);
266 }
267
268 if (!database)
269 return;
270
271 database->performCallbackOnOriginThread(*database, &IDBDatabase::fireVersionChangeEvent, requestIdentifier, requestedVersion);
272}
273
274void IDBConnectionProxy::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
275{
276 callConnectionOnMainThread(&IDBConnectionToServer::didFireVersionChangeEvent, databaseConnectionIdentifier, requestIdentifier);
277}
278
279void IDBConnectionProxy::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion)
280{
281 ASSERT(isMainThread());
282
283 RefPtr<IDBOpenDBRequest> request;
284 {
285 Locker<Lock> locker(m_openDBRequestMapLock);
286 request = m_openDBRequestMap.get(requestIdentifier);
287 }
288
289 if (!request)
290 return;
291
292 request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestBlocked, oldVersion, newVersion);
293}
294
295void IDBConnectionProxy::openDBRequestCancelled(const IDBRequestData& requestData)
296{
297 callConnectionOnMainThread(&IDBConnectionToServer::openDBRequestCancelled, requestData);
298}
299
300void IDBConnectionProxy::establishTransaction(IDBTransaction& transaction)
301{
302 {
303 Locker<Lock> locker(m_transactionMapLock);
304 ASSERT(!hasRecordOfTransaction(transaction));
305 m_pendingTransactions.set(transaction.info().identifier(), &transaction);
306 }
307
308 callConnectionOnMainThread(&IDBConnectionToServer::establishTransaction, transaction.database().databaseConnectionIdentifier(), transaction.info());
309}
310
311void IDBConnectionProxy::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
312{
313 RefPtr<IDBTransaction> transaction;
314 {
315 Locker<Lock> locker(m_transactionMapLock);
316 transaction = m_pendingTransactions.take(transactionIdentifier);
317 }
318
319 if (!transaction)
320 return;
321
322 transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didStart, error);
323}
324
325void IDBConnectionProxy::commitTransaction(IDBTransaction& transaction)
326{
327 {
328 Locker<Lock> locker(m_transactionMapLock);
329 ASSERT(!m_committingTransactions.contains(transaction.info().identifier()));
330 m_committingTransactions.set(transaction.info().identifier(), &transaction);
331 }
332
333 callConnectionOnMainThread(&IDBConnectionToServer::commitTransaction, transaction.info().identifier());
334}
335
336void IDBConnectionProxy::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
337{
338 RefPtr<IDBTransaction> transaction;
339 {
340 Locker<Lock> locker(m_transactionMapLock);
341 transaction = m_committingTransactions.take(transactionIdentifier);
342 }
343
344 if (!transaction)
345 return;
346
347 transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didCommit, error);
348}
349
350void IDBConnectionProxy::abortTransaction(IDBTransaction& transaction)
351{
352 {
353 Locker<Lock> locker(m_transactionMapLock);
354 ASSERT(!m_abortingTransactions.contains(transaction.info().identifier()));
355 m_abortingTransactions.set(transaction.info().identifier(), &transaction);
356 }
357
358 callConnectionOnMainThread(&IDBConnectionToServer::abortTransaction, transaction.info().identifier());
359}
360
361void IDBConnectionProxy::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
362{
363 RefPtr<IDBTransaction> transaction;
364 {
365 Locker<Lock> locker(m_transactionMapLock);
366 transaction = m_abortingTransactions.take(transactionIdentifier);
367 }
368
369 if (!transaction)
370 return;
371
372 transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didAbort, error);
373}
374
375bool IDBConnectionProxy::hasRecordOfTransaction(const IDBTransaction& transaction) const
376{
377 ASSERT(m_transactionMapLock.isLocked());
378
379 auto identifier = transaction.info().identifier();
380 return m_pendingTransactions.contains(identifier) || m_committingTransactions.contains(identifier) || m_abortingTransactions.contains(identifier);
381}
382
383void IDBConnectionProxy::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction& transaction)
384{
385 callConnectionOnMainThread(&IDBConnectionToServer::didFinishHandlingVersionChangeTransaction, databaseConnectionIdentifier, transaction.info().identifier());
386}
387
388void IDBConnectionProxy::databaseConnectionPendingClose(IDBDatabase& database)
389{
390 callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionPendingClose, database.databaseConnectionIdentifier());
391}
392
393void IDBConnectionProxy::databaseConnectionClosed(IDBDatabase& database)
394{
395 callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionClosed, database.databaseConnectionIdentifier());
396}
397
398void IDBConnectionProxy::didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError& error)
399{
400 RefPtr<IDBDatabase> database;
401 {
402 Locker<Lock> locker(m_databaseConnectionMapLock);
403 database = m_databaseConnectionMap.get(databaseConnectionIdentifier);
404 }
405
406 // If the IDBDatabase object is gone, message back to the server so it doesn't hang
407 // waiting for a reply that will never come.
408 if (!database) {
409 m_connectionToServer.confirmDidCloseFromServer(databaseConnectionIdentifier);
410 return;
411 }
412
413 database->performCallbackOnOriginThread(*database, &IDBDatabase::didCloseFromServer, error);
414}
415
416void IDBConnectionProxy::confirmDidCloseFromServer(IDBDatabase& database)
417{
418 callConnectionOnMainThread(&IDBConnectionToServer::confirmDidCloseFromServer, database.databaseConnectionIdentifier());
419}
420
421void IDBConnectionProxy::connectionToServerLost(const IDBError& error)
422{
423 Vector<uint64_t> databaseConnectionIdentifiers;
424 {
425 Locker<Lock> locker(m_databaseConnectionMapLock);
426 databaseConnectionIdentifiers = copyToVector(m_databaseConnectionMap.keys());
427 }
428
429 for (auto connectionIdentifier : databaseConnectionIdentifiers) {
430 RefPtr<IDBDatabase> database;
431 {
432 Locker<Lock> locker(m_databaseConnectionMapLock);
433 database = m_databaseConnectionMap.get(connectionIdentifier);
434 }
435
436 if (!database)
437 continue;
438
439 database->performCallbackOnOriginThread(*database, &IDBDatabase::connectionToServerLost, error);
440 }
441
442 Vector<IDBResourceIdentifier> openDBRequestIdentifiers;
443 {
444 Locker<Lock> locker(m_openDBRequestMapLock);
445 openDBRequestIdentifiers = copyToVector(m_openDBRequestMap.keys());
446 }
447
448 for (auto& requestIdentifier : openDBRequestIdentifiers) {
449 RefPtr<IDBOpenDBRequest> request;
450 {
451 Locker<Lock> locker(m_openDBRequestMapLock);
452 request = m_openDBRequestMap.get(requestIdentifier);
453 }
454
455 if (!request)
456 continue;
457
458 auto result = IDBResultData::error(requestIdentifier, error);
459 request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, result);
460 }
461}
462
463void IDBConnectionProxy::scheduleMainThreadTasks()
464{
465 Locker<Lock> locker(m_mainThreadTaskLock);
466 if (m_mainThreadProtector)
467 return;
468
469 m_mainThreadProtector = &m_connectionToServer;
470 callOnMainThread([this] {
471 handleMainThreadTasks();
472 });
473}
474
475void IDBConnectionProxy::handleMainThreadTasks()
476{
477 RefPtr<IDBConnectionToServer> protector;
478 {
479 Locker<Lock> locker(m_mainThreadTaskLock);
480 ASSERT(m_mainThreadProtector);
481 protector = WTFMove(m_mainThreadProtector);
482 }
483
484 while (auto task = m_mainThreadQueue.tryGetMessage())
485 task->performTask();
486}
487
488void IDBConnectionProxy::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, Function<void (const Vector<String>&)>&& callback)
489{
490 // This method is only meant to be called by the web inspector on the main thread.
491 RELEASE_ASSERT(isMainThread());
492
493 m_connectionToServer.getAllDatabaseNames(mainFrameOrigin, openingOrigin, WTFMove(callback));
494}
495
496void IDBConnectionProxy::registerDatabaseConnection(IDBDatabase& database)
497{
498 Locker<Lock> locker(m_databaseConnectionMapLock);
499
500 ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()));
501 m_databaseConnectionMap.set(database.databaseConnectionIdentifier(), &database);
502}
503
504void IDBConnectionProxy::unregisterDatabaseConnection(IDBDatabase& database)
505{
506 Locker<Lock> locker(m_databaseConnectionMapLock);
507
508 ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()) || m_databaseConnectionMap.get(database.databaseConnectionIdentifier()) == &database);
509 m_databaseConnectionMap.remove(database.databaseConnectionIdentifier());
510}
511
512void IDBConnectionProxy::forgetActiveOperations(const Vector<RefPtr<TransactionOperation>>& operations)
513{
514 Locker<Lock> locker(m_transactionOperationLock);
515
516 for (auto& operation : operations)
517 m_activeOperations.remove(operation->identifier());
518}
519
520void IDBConnectionProxy::forgetTransaction(IDBTransaction& transaction)
521{
522 Locker<Lock> locker(m_transactionMapLock);
523
524 m_pendingTransactions.remove(transaction.info().identifier());
525 m_committingTransactions.remove(transaction.info().identifier());
526 m_abortingTransactions.remove(transaction.info().identifier());
527}
528
529template<typename KeyType, typename ValueType>
530void removeItemsMatchingCurrentThread(HashMap<KeyType, ValueType>& map)
531{
532 auto& currentThread = Thread::current();
533
534 Vector<KeyType> keys;
535 keys.reserveInitialCapacity(map.size());
536 for (auto& iterator : map) {
537 if (&iterator.value->originThread() == &currentThread)
538 keys.uncheckedAppend(iterator.key);
539 }
540
541 for (auto& key : keys)
542 map.remove(key);
543}
544
545void IDBConnectionProxy::forgetActivityForCurrentThread()
546{
547 ASSERT(!isMainThread());
548
549 {
550 Locker<Lock> lock(m_databaseConnectionMapLock);
551 removeItemsMatchingCurrentThread(m_databaseConnectionMap);
552 }
553 {
554 Locker<Lock> lock(m_openDBRequestMapLock);
555 removeItemsMatchingCurrentThread(m_openDBRequestMap);
556 }
557 {
558 Locker<Lock> lock(m_transactionMapLock);
559 removeItemsMatchingCurrentThread(m_pendingTransactions);
560 removeItemsMatchingCurrentThread(m_committingTransactions);
561 removeItemsMatchingCurrentThread(m_abortingTransactions);
562 }
563 {
564 Locker<Lock> lock(m_transactionOperationLock);
565 removeItemsMatchingCurrentThread(m_activeOperations);
566 }
567}
568
569} // namesapce IDBClient
570} // namespace WebCore
571
572#endif // ENABLE(INDEXED_DATABASE)
573