1/*
2 * Copyright (C) 2007, 2008, 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 *
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 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "Database.h"
31
32#include "ChangeVersionData.h"
33#include "ChangeVersionWrapper.h"
34#include "DOMWindow.h"
35#include "DatabaseAuthorizer.h"
36#include "DatabaseCallback.h"
37#include "DatabaseContext.h"
38#include "DatabaseManager.h"
39#include "DatabaseTask.h"
40#include "DatabaseThread.h"
41#include "DatabaseTracker.h"
42#include "Document.h"
43#include "JSDOMWindow.h"
44#include "Logging.h"
45#include "SQLError.h"
46#include "SQLTransaction.h"
47#include "SQLTransactionCallback.h"
48#include "SQLTransactionErrorCallback.h"
49#include "SQLiteDatabaseTracker.h"
50#include "SQLiteStatement.h"
51#include "SQLiteTransaction.h"
52#include "ScriptExecutionContext.h"
53#include "SecurityOrigin.h"
54#include "VoidCallback.h"
55#include <wtf/NeverDestroyed.h>
56#include <wtf/RefPtr.h>
57#include <wtf/StdLibExtras.h>
58#include <wtf/text/CString.h>
59
60namespace WebCore {
61
62// Registering "opened" databases with the DatabaseTracker
63// =======================================================
64// The DatabaseTracker maintains a list of databases that have been
65// "opened" so that the client can call interrupt or delete on every database
66// associated with a DatabaseContext.
67//
68// We will only call DatabaseTracker::addOpenDatabase() to add the database
69// to the tracker as opened when we've succeeded in opening the database,
70// and will set m_opened to true. Similarly, we only call
71// DatabaseTracker::removeOpenDatabase() to remove the database from the
72// tracker when we set m_opened to false in closeDatabase(). This sets up
73// a simple symmetry between open and close operations, and a direct
74// correlation to adding and removing databases from the tracker's list,
75// thus ensuring that we have a correct list for the interrupt and
76// delete operations to work on.
77//
78// The only databases instances not tracked by the tracker's open database
79// list are the ones that have not been added yet, or the ones that we
80// attempted an open on but failed to. Such instances only exist in the
81// factory functions for creating database backends.
82//
83// The factory functions will either call openAndVerifyVersion() or
84// performOpenAndVerify(). These methods will add the newly instantiated
85// database backend if they succeed in opening the requested database.
86// In the case of failure to open the database, the factory methods will
87// simply discard the newly instantiated database backend when they return.
88// The ref counting mechanims will automatically destruct the un-added
89// (and un-returned) databases instances.
90
91static const char versionKey[] = "WebKitDatabaseVersionKey";
92static const char unqualifiedInfoTableName[] = "__WebKitDatabaseInfoTable__";
93const unsigned long long quotaIncreaseSize = 5 * 1024 * 1024;
94
95static const char* fullyQualifiedInfoTableName()
96{
97 static const char qualifier[] = "main.";
98 static char qualifiedName[sizeof(qualifier) + sizeof(unqualifiedInfoTableName) - 1];
99
100 static std::once_flag onceFlag;
101 std::call_once(onceFlag, [] {
102 snprintf(qualifiedName, sizeof(qualifiedName), "%s%s", qualifier, unqualifiedInfoTableName);
103 });
104
105 return qualifiedName;
106}
107
108static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage)
109{
110 return makeString(message, " (", sqliteErrorCode, ' ', sqliteErrorMessage, ')');
111}
112
113static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
114{
115 SQLiteStatement statement(db, query);
116 int result = statement.prepare();
117
118 if (result != SQLITE_OK) {
119 LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
120 return false;
121 }
122
123 statement.bindText(1, value);
124
125 result = statement.step();
126 if (result != SQLITE_DONE) {
127 LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
128 return false;
129 }
130
131 return true;
132}
133
134static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
135{
136 SQLiteStatement statement(db, query);
137 int result = statement.prepare();
138
139 if (result != SQLITE_OK) {
140 LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
141 return false;
142 }
143
144 result = statement.step();
145 if (result == SQLITE_ROW) {
146 resultString = statement.getColumnText(0);
147 return true;
148 }
149 if (result == SQLITE_DONE) {
150 resultString = String();
151 return true;
152 }
153
154 LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
155 return false;
156}
157
158// FIXME: move all guid-related functions to a DatabaseVersionTracker class.
159static Lock guidMutex;
160
161static HashMap<DatabaseGUID, String>& guidToVersionMap()
162{
163 static NeverDestroyed<HashMap<DatabaseGUID, String>> map;
164 return map;
165}
166
167// NOTE: Caller must lock guidMutex().
168static inline void updateGUIDVersionMap(DatabaseGUID guid, const String& newVersion)
169{
170 // Note: It is not safe to put an empty string into the guidToVersionMap() map.
171 // That's because the map is cross-thread, but empty strings are per-thread.
172 // The copy() function makes a version of the string you can use on the current
173 // thread, but we need a string we can keep in a cross-thread data structure.
174 // FIXME: This is a quite-awkward restriction to have to program with.
175
176 // Map empty string to null string (see comment above).
177 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy());
178}
179
180static HashMap<DatabaseGUID, HashSet<Database*>>& guidToDatabaseMap()
181{
182 static NeverDestroyed<HashMap<DatabaseGUID, HashSet<Database*>>> map;
183 return map;
184}
185
186static inline DatabaseGUID guidForOriginAndName(const String& origin, const String& name)
187{
188 static NeverDestroyed<HashMap<String, DatabaseGUID>> map;
189 return map.get().ensure(makeString(origin, '/', name), [] {
190 static DatabaseGUID lastUsedGUID;
191 return ++lastUsedGUID;
192 }).iterator->value;
193}
194
195Database::Database(DatabaseContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned long long estimatedSize)
196 : m_scriptExecutionContext(*context.scriptExecutionContext())
197 , m_contextThreadSecurityOrigin(m_scriptExecutionContext->securityOrigin()->isolatedCopy())
198 , m_databaseThreadSecurityOrigin(m_scriptExecutionContext->securityOrigin()->isolatedCopy())
199 , m_databaseContext(context)
200 , m_name((name.isNull() ? emptyString() : name).isolatedCopy())
201 , m_expectedVersion(expectedVersion.isolatedCopy())
202 , m_displayName(displayName.isolatedCopy())
203 , m_estimatedSize(estimatedSize)
204 , m_filename(DatabaseManager::singleton().fullPathForDatabase(*m_scriptExecutionContext->securityOrigin(), m_name))
205 , m_databaseAuthorizer(DatabaseAuthorizer::create(unqualifiedInfoTableName))
206{
207 {
208 std::lock_guard<Lock> locker(guidMutex);
209
210 m_guid = guidForOriginAndName(securityOrigin().securityOrigin()->toString(), name);
211 guidToDatabaseMap().ensure(m_guid, [] {
212 return HashSet<Database*>();
213 }).iterator->value.add(this);
214 }
215
216 m_databaseContext->databaseThread();
217
218 ASSERT(m_databaseContext->existingDatabaseThread());
219}
220
221DatabaseThread& Database::databaseThread()
222{
223 ASSERT(m_databaseContext->existingDatabaseThread());
224 return *m_databaseContext->existingDatabaseThread();
225}
226
227Database::~Database()
228{
229 // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing.
230 if (!m_scriptExecutionContext->isContextThread()) {
231 auto passedContext = WTFMove(m_scriptExecutionContext);
232 auto& contextRef = passedContext.get();
233 contextRef.postTask({ScriptExecutionContext::Task::CleanupTask, [passedContext = WTFMove(passedContext), databaseContext = WTFMove(m_databaseContext)] (ScriptExecutionContext& context) {
234 ASSERT_UNUSED(context, &context == passedContext.ptr());
235 }});
236 }
237
238 // SQLite is "multi-thread safe", but each database handle can only be used
239 // on a single thread at a time.
240 //
241 // For DatabaseBackend, we open the SQLite database on the DatabaseThread,
242 // and hence we should also close it on that same thread. This means that the
243 // SQLite database need to be closed by another mechanism (see
244 // DatabaseContext::stopDatabases()). By the time we get here, the SQLite
245 // database should have already been closed.
246
247 ASSERT(!m_opened);
248}
249
250ExceptionOr<void> Database::openAndVerifyVersion(bool setVersionInNewDatabase)
251{
252 DatabaseTaskSynchronizer synchronizer;
253 auto& thread = databaseThread();
254 if (thread.terminationRequested(&synchronizer))
255 return Exception { InvalidStateError };
256
257 ExceptionOr<void> result;
258 auto task = std::make_unique<DatabaseOpenTask>(*this, setVersionInNewDatabase, synchronizer, result);
259 thread.scheduleImmediateTask(WTFMove(task));
260 synchronizer.waitForTaskCompletion();
261
262 return result;
263}
264
265void Database::interrupt()
266{
267 // It is safe to call this from any thread for an opened or closed database.
268 m_sqliteDatabase.interrupt();
269}
270
271void Database::close()
272{
273 auto& thread = databaseThread();
274
275 DatabaseTaskSynchronizer synchronizer;
276 if (thread.terminationRequested(&synchronizer)) {
277 LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
278 return;
279 }
280
281 thread.scheduleImmediateTask(std::make_unique<DatabaseCloseTask>(*this, synchronizer));
282
283 // FIXME: iOS depends on this function blocking until the database is closed as part
284 // of closing all open databases from a process assertion expiration handler.
285 // See <https://bugs.webkit.org/show_bug.cgi?id=157184>.
286 synchronizer.waitForTaskCompletion();
287}
288
289void Database::performClose()
290{
291 ASSERT(databaseThread().getThread() == &Thread::current());
292
293 {
294 LockHolder locker(m_transactionInProgressMutex);
295
296 // Clean up transactions that have not been scheduled yet:
297 // Transaction phase 1 cleanup. See comment on "What happens if a
298 // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
299 while (!m_transactionQueue.isEmpty())
300 m_transactionQueue.takeFirst()->notifyDatabaseThreadIsShuttingDown();
301
302 m_isTransactionQueueEnabled = false;
303 m_transactionInProgress = false;
304 }
305
306 closeDatabase();
307
308 // DatabaseThread keeps databases alive by referencing them in its
309 // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove
310 // this database from that set (which effectively deref's it). We hold on
311 // to it with a local pointer here for a liitle longer, so that we can
312 // unschedule any DatabaseTasks that refer to it before the database gets
313 // deleted.
314 Ref<Database> protectedThis(*this);
315 auto& thread = databaseThread();
316 thread.recordDatabaseClosed(*this);
317 thread.unscheduleDatabaseTasks(*this);
318}
319
320class DoneCreatingDatabaseOnExitCaller {
321public:
322 DoneCreatingDatabaseOnExitCaller(Database& database)
323 : m_database(database)
324 {
325 }
326
327 ~DoneCreatingDatabaseOnExitCaller()
328 {
329 DatabaseTracker::singleton().doneCreatingDatabase(m_database);
330 }
331
332private:
333 Database& m_database;
334};
335
336ExceptionOr<void> Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase)
337{
338 DoneCreatingDatabaseOnExitCaller onExitCaller(*this);
339
340 const int maxSqliteBusyWaitTime = 30000;
341
342#if PLATFORM(IOS_FAMILY)
343 {
344 // Make sure we wait till the background removal of the empty database files finished before trying to open any database.
345 auto locker = holdLock(DatabaseTracker::openDatabaseMutex());
346 }
347#endif
348
349 SQLiteTransactionInProgressAutoCounter transactionCounter;
350
351 if (!m_sqliteDatabase.open(m_filename))
352 return Exception { InvalidStateError, formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()) };
353 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum())
354 LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
355
356 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
357
358 String currentVersion;
359 {
360 std::lock_guard<Lock> locker(guidMutex);
361
362 auto entry = guidToVersionMap().find(m_guid);
363 if (entry != guidToVersionMap().end()) {
364 // Map null string to empty string (see updateGUIDVersionMap()).
365 currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy();
366 LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
367 } else {
368 LOG(StorageAPI, "No cached version for guid %i", m_guid);
369
370 SQLiteTransaction transaction(m_sqliteDatabase);
371 transaction.begin();
372 if (!transaction.inProgress()) {
373 String message = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
374 m_sqliteDatabase.close();
375 return Exception { InvalidStateError, WTFMove(message) };
376 }
377
378 String tableName(unqualifiedInfoTableName);
379 if (!m_sqliteDatabase.tableExists(tableName)) {
380 m_new = true;
381
382 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
383 String message = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
384 transaction.rollback();
385 m_sqliteDatabase.close();
386 return Exception { InvalidStateError, WTFMove(message) };
387 }
388 } else if (!getVersionFromDatabase(currentVersion, false)) {
389 String message = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
390 transaction.rollback();
391 m_sqliteDatabase.close();
392 return Exception { InvalidStateError, WTFMove(message) };
393 }
394
395 if (currentVersion.length()) {
396 LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
397 } else if (!m_new || shouldSetVersionInNewDatabase) {
398 LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
399 if (!setVersionInDatabase(m_expectedVersion, false)) {
400 String message = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
401 transaction.rollback();
402 m_sqliteDatabase.close();
403 return Exception { InvalidStateError, WTFMove(message) };
404 }
405 currentVersion = m_expectedVersion;
406 }
407 updateGUIDVersionMap(m_guid, currentVersion);
408 transaction.commit();
409 }
410 }
411
412 if (currentVersion.isNull()) {
413 LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
414 currentVersion = emptyString();
415 }
416
417 // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception.
418 // If the expected version is the empty string, then we always return with whatever version of the database we have.
419 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
420 m_sqliteDatabase.close();
421 return Exception { InvalidStateError, "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'" };
422 }
423
424 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get());
425
426 DatabaseTracker::singleton().addOpenDatabase(*this);
427 m_opened = true;
428
429 if (m_new && !shouldSetVersionInNewDatabase)
430 m_expectedVersion = emptyString(); // The caller provided a creationCallback which will set the expected version.
431
432 databaseThread().recordDatabaseOpen(*this);
433
434 return { };
435}
436
437void Database::closeDatabase()
438{
439 if (!m_opened)
440 return;
441
442 m_sqliteDatabase.close();
443 m_opened = false;
444
445 // See comment at the top this file regarding calling removeOpenDatabase().
446 DatabaseTracker::singleton().removeOpenDatabase(*this);
447
448 {
449 std::lock_guard<Lock> locker(guidMutex);
450
451 auto it = guidToDatabaseMap().find(m_guid);
452 ASSERT(it != guidToDatabaseMap().end());
453 ASSERT(it->value.contains(this));
454 it->value.remove(this);
455 if (it->value.isEmpty()) {
456 guidToDatabaseMap().remove(it);
457 guidToVersionMap().remove(m_guid);
458 }
459 }
460}
461
462bool Database::getVersionFromDatabase(String& version, bool shouldCacheVersion)
463{
464 String query(String("SELECT value FROM ") + fullyQualifiedInfoTableName() + " WHERE key = '" + versionKey + "';");
465
466 m_databaseAuthorizer->disable();
467
468 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version);
469 if (result) {
470 if (shouldCacheVersion)
471 setCachedVersion(version);
472 } else
473 LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
474
475 m_databaseAuthorizer->enable();
476
477 return result;
478}
479
480bool Database::setVersionInDatabase(const String& version, bool shouldCacheVersion)
481{
482 // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE
483 // clause in the CREATE statement (see Database::performOpenAndVerify()).
484 String query(String("INSERT INTO ") + fullyQualifiedInfoTableName() + " (key, value) VALUES ('" + versionKey + "', ?);");
485
486 m_databaseAuthorizer->disable();
487
488 bool result = setTextValueInDatabase(m_sqliteDatabase, query, version);
489 if (result) {
490 if (shouldCacheVersion)
491 setCachedVersion(version);
492 } else
493 LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data());
494
495 m_databaseAuthorizer->enable();
496
497 return result;
498}
499
500void Database::setExpectedVersion(const String& version)
501{
502 m_expectedVersion = version.isolatedCopy();
503}
504
505String Database::getCachedVersion() const
506{
507 std::lock_guard<Lock> locker(guidMutex);
508
509 return guidToVersionMap().get(m_guid).isolatedCopy();
510}
511
512void Database::setCachedVersion(const String& actualVersion)
513{
514 std::lock_guard<Lock> locker(guidMutex);
515
516 updateGUIDVersionMap(m_guid, actualVersion);
517}
518
519bool Database::getActualVersionForTransaction(String &actualVersion)
520{
521 ASSERT(m_sqliteDatabase.transactionInProgress());
522
523 // Note: In multi-process browsers the cached value may be inaccurate.
524 // So we retrieve the value from the database and update the cached value here.
525 return getVersionFromDatabase(actualVersion, true);
526}
527
528void Database::scheduleTransaction()
529{
530 ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
531
532 if (!m_isTransactionQueueEnabled || m_transactionQueue.isEmpty()) {
533 m_transactionInProgress = false;
534 return;
535 }
536
537 m_transactionInProgress = true;
538
539 auto transaction = m_transactionQueue.takeFirst();
540 auto task = std::make_unique<DatabaseTransactionTask>(WTFMove(transaction));
541 LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
542 databaseThread().scheduleTask(WTFMove(task));
543}
544
545void Database::scheduleTransactionStep(SQLTransaction& transaction)
546{
547 auto& thread = databaseThread();
548
549 auto task = std::make_unique<DatabaseTransactionTask>(&transaction);
550 LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
551 thread.scheduleTask(WTFMove(task));
552}
553
554void Database::inProgressTransactionCompleted()
555{
556 LockHolder locker(m_transactionInProgressMutex);
557 m_transactionInProgress = false;
558 scheduleTransaction();
559}
560
561bool Database::hasPendingTransaction()
562{
563 LockHolder locker(m_transactionInProgressMutex);
564 return m_transactionInProgress || !m_transactionQueue.isEmpty();
565}
566
567SQLTransactionCoordinator* Database::transactionCoordinator()
568{
569 return databaseThread().transactionCoordinator();
570}
571
572String Database::version() const
573{
574 if (m_deleted)
575 return String();
576
577 // Note: In multi-process browsers the cached value may be accurate, but we cannot read the
578 // actual version from the database without potentially inducing a deadlock.
579 // FIXME: Add an async version getter to the DatabaseAPI.
580 return getCachedVersion();
581}
582
583void Database::markAsDeletedAndClose()
584{
585 if (m_deleted)
586 return;
587
588 LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
589 m_deleted = true;
590
591 close();
592}
593
594void Database::changeVersion(const String& oldVersion, const String& newVersion, RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback)
595{
596 runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), ChangeVersionWrapper::create(oldVersion, newVersion), false);
597}
598
599void Database::transaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback)
600{
601 runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), nullptr, false);
602}
603
604void Database::readTransaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback)
605{
606 runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), nullptr, true);
607}
608
609String Database::stringIdentifier() const
610{
611 // Return a deep copy for ref counting thread safety
612 return m_name.isolatedCopy();
613}
614
615String Database::displayName() const
616{
617 // Return a deep copy for ref counting thread safety
618 return m_displayName.isolatedCopy();
619}
620
621String Database::expectedVersion() const
622{
623 // Return a deep copy for ref counting thread safety
624 return m_expectedVersion.isolatedCopy();
625}
626
627unsigned long long Database::estimatedSize() const
628{
629 return m_estimatedSize;
630}
631
632void Database::setEstimatedSize(unsigned long long estimatedSize)
633{
634 m_estimatedSize = estimatedSize;
635 DatabaseTracker::singleton().setDatabaseDetails(securityOrigin(), m_name, m_displayName, m_estimatedSize);
636}
637
638String Database::fileName() const
639{
640 // Return a deep copy for ref counting thread safety
641 return m_filename.isolatedCopy();
642}
643
644DatabaseDetails Database::details() const
645{
646 // This code path is only used for database quota delegate calls, so file dates are irrelevant and left uninitialized.
647 return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0, WTF::nullopt, WTF::nullopt);
648}
649
650void Database::disableAuthorizer()
651{
652 m_databaseAuthorizer->disable();
653}
654
655void Database::enableAuthorizer()
656{
657 m_databaseAuthorizer->enable();
658}
659
660void Database::setAuthorizerPermissions(int permissions)
661{
662 m_databaseAuthorizer->setPermissions(permissions);
663}
664
665bool Database::lastActionChangedDatabase()
666{
667 return m_databaseAuthorizer->lastActionChangedDatabase();
668}
669
670bool Database::lastActionWasInsert()
671{
672 return m_databaseAuthorizer->lastActionWasInsert();
673}
674
675void Database::resetDeletes()
676{
677 m_databaseAuthorizer->resetDeletes();
678}
679
680bool Database::hadDeletes()
681{
682 return m_databaseAuthorizer->hadDeletes();
683}
684
685void Database::resetAuthorizer()
686{
687 m_databaseAuthorizer->reset();
688}
689
690void Database::runTransaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly)
691{
692 LockHolder locker(m_transactionInProgressMutex);
693 if (!m_isTransactionQueueEnabled) {
694 if (errorCallback) {
695 RefPtr<SQLTransactionErrorCallback> errorCallbackProtector = WTFMove(errorCallback);
696 m_scriptExecutionContext->postTask([errorCallbackProtector](ScriptExecutionContext&) {
697 errorCallbackProtector->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed"));
698 });
699 }
700 return;
701 }
702
703 m_transactionQueue.append(SQLTransaction::create(*this, WTFMove(callback), WTFMove(successCallback), errorCallback.copyRef(), WTFMove(wrapper), readOnly));
704 if (!m_transactionInProgress)
705 scheduleTransaction();
706}
707
708void Database::scheduleTransactionCallback(SQLTransaction* transaction)
709{
710 RefPtr<SQLTransaction> transactionProtector(transaction);
711 m_scriptExecutionContext->postTask([transactionProtector] (ScriptExecutionContext&) {
712 transactionProtector->performPendingCallback();
713 });
714}
715
716Vector<String> Database::performGetTableNames()
717{
718 disableAuthorizer();
719
720 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
721 if (statement.prepare() != SQLITE_OK) {
722 LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
723 enableAuthorizer();
724 return Vector<String>();
725 }
726
727 Vector<String> tableNames;
728 int result;
729 while ((result = statement.step()) == SQLITE_ROW) {
730 String name = statement.getColumnText(0);
731 if (name != unqualifiedInfoTableName)
732 tableNames.append(name);
733 }
734
735 enableAuthorizer();
736
737 if (result != SQLITE_DONE) {
738 LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
739 return Vector<String>();
740 }
741
742 return tableNames;
743}
744
745void Database::incrementalVacuumIfNeeded()
746{
747 SQLiteTransactionInProgressAutoCounter transactionCounter;
748
749 int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
750 int64_t totalSize = m_sqliteDatabase.totalSize();
751 if (totalSize <= 10 * freeSpaceSize) {
752 int result = m_sqliteDatabase.runIncrementalVacuumCommand();
753 if (result != SQLITE_OK)
754 logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg()));
755 }
756}
757
758void Database::logErrorMessage(const String& message)
759{
760 m_scriptExecutionContext->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);
761}
762
763Vector<String> Database::tableNames()
764{
765 // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
766 // in dealing with them. However, if the code changes, this may not be true anymore.
767 Vector<String> result;
768 DatabaseTaskSynchronizer synchronizer;
769 auto& thread = databaseThread();
770 if (thread.terminationRequested(&synchronizer))
771 return result;
772
773 auto task = std::make_unique<DatabaseTableNamesTask>(*this, synchronizer, result);
774 thread.scheduleImmediateTask(WTFMove(task));
775 synchronizer.waitForTaskCompletion();
776
777 return result;
778}
779
780SecurityOriginData Database::securityOrigin()
781{
782 if (m_scriptExecutionContext->isContextThread())
783 return m_contextThreadSecurityOrigin->data();
784 if (databaseThread().getThread() == &Thread::current())
785 return m_databaseThreadSecurityOrigin->data();
786 RELEASE_ASSERT_NOT_REACHED();
787}
788
789unsigned long long Database::maximumSize()
790{
791 return DatabaseTracker::singleton().maximumSize(*this);
792}
793
794void Database::didCommitWriteTransaction()
795{
796 DatabaseTracker::singleton().scheduleNotifyDatabaseChanged(securityOrigin(), stringIdentifier());
797}
798
799bool Database::didExceedQuota()
800{
801 ASSERT(databaseContext().scriptExecutionContext()->isContextThread());
802 auto& tracker = DatabaseTracker::singleton();
803 auto oldQuota = tracker.quota(securityOrigin());
804 if (estimatedSize() <= oldQuota) {
805 // The expected usage provided by the page is now smaller than the actual database size so we bump the expected usage to
806 // oldQuota + 5MB so that the client actually increases the quota.
807 setEstimatedSize(oldQuota + quotaIncreaseSize);
808 }
809 databaseContext().databaseExceededQuota(stringIdentifier(), details());
810 return tracker.quota(securityOrigin()) > oldQuota;
811}
812
813#if !LOG_DISABLED || !ERROR_DISABLED
814
815String Database::databaseDebugName() const
816{
817 return m_contextThreadSecurityOrigin->toString() + "::" + m_name;
818}
819
820#endif
821
822} // namespace WebCore
823