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