• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "modules/webdatabase/Database.h"
28 
29 #include "core/dom/CrossThreadTask.h"
30 #include "core/dom/ExceptionCode.h"
31 #include "core/html/VoidCallback.h"
32 #include "modules/webdatabase/ChangeVersionData.h"
33 #include "modules/webdatabase/ChangeVersionWrapper.h"
34 #include "modules/webdatabase/DatabaseAuthorizer.h"
35 #include "modules/webdatabase/DatabaseContext.h"
36 #include "modules/webdatabase/DatabaseManager.h"
37 #include "modules/webdatabase/DatabaseTask.h"
38 #include "modules/webdatabase/DatabaseThread.h"
39 #include "modules/webdatabase/DatabaseTracker.h"
40 #include "modules/webdatabase/SQLError.h"
41 #include "modules/webdatabase/SQLTransaction.h"
42 #include "modules/webdatabase/SQLTransactionBackend.h"
43 #include "modules/webdatabase/SQLTransactionCallback.h"
44 #include "modules/webdatabase/SQLTransactionClient.h"
45 #include "modules/webdatabase/SQLTransactionCoordinator.h"
46 #include "modules/webdatabase/SQLTransactionErrorCallback.h"
47 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
48 #include "modules/webdatabase/sqlite/SQLiteTransaction.h"
49 #include "platform/Logging.h"
50 #include "platform/weborigin/DatabaseIdentifier.h"
51 #include "public/platform/Platform.h"
52 #include "public/platform/WebDatabaseObserver.h"
53 
54 // Registering "opened" databases with the DatabaseTracker
55 // =======================================================
56 // The DatabaseTracker maintains a list of databases that have been
57 // "opened" so that the client can call interrupt or delete on every database
58 // associated with a DatabaseContext.
59 //
60 // We will only call DatabaseTracker::addOpenDatabase() to add the database
61 // to the tracker as opened when we've succeeded in opening the database,
62 // and will set m_opened to true. Similarly, we only call
63 // DatabaseTracker::removeOpenDatabase() to remove the database from the
64 // tracker when we set m_opened to false in closeDatabase(). This sets up
65 // a simple symmetry between open and close operations, and a direct
66 // correlation to adding and removing databases from the tracker's list,
67 // thus ensuring that we have a correct list for the interrupt and
68 // delete operations to work on.
69 //
70 // The only databases instances not tracked by the tracker's open database
71 // list are the ones that have not been added yet, or the ones that we
72 // attempted an open on but failed to. Such instances only exist in the
73 // DatabaseServer's factory methods for creating database backends.
74 //
75 // The factory methods will either call openAndVerifyVersion() or
76 // performOpenAndVerify(). These methods will add the newly instantiated
77 // database backend if they succeed in opening the requested database.
78 // In the case of failure to open the database, the factory methods will
79 // simply discard the newly instantiated database backend when they return.
80 // The ref counting mechanims will automatically destruct the un-added
81 // (and un-returned) databases instances.
82 
83 namespace blink {
84 
85 static const char versionKey[] = "WebKitDatabaseVersionKey";
86 static const char infoTableName[] = "__WebKitDatabaseInfoTable__";
87 
formatErrorMessage(const char * message,int sqliteErrorCode,const char * sqliteErrorMessage)88 static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage)
89 {
90     return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage);
91 }
92 
retrieveTextResultFromDatabase(SQLiteDatabase & db,const String & query,String & resultString)93 static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
94 {
95     SQLiteStatement statement(db, query);
96     int result = statement.prepare();
97 
98     if (result != SQLResultOk) {
99         WTF_LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
100         return false;
101     }
102 
103     result = statement.step();
104     if (result == SQLResultRow) {
105         resultString = statement.getColumnText(0);
106         return true;
107     }
108     if (result == SQLResultDone) {
109         resultString = String();
110         return true;
111     }
112 
113     WTF_LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
114     return false;
115 }
116 
setTextValueInDatabase(SQLiteDatabase & db,const String & query,const String & value)117 static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
118 {
119     SQLiteStatement statement(db, query);
120     int result = statement.prepare();
121 
122     if (result != SQLResultOk) {
123         WTF_LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
124         return false;
125     }
126 
127     statement.bindText(1, value);
128 
129     result = statement.step();
130     if (result != SQLResultDone) {
131         WTF_LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
132         return false;
133     }
134 
135     return true;
136 }
137 
138 // FIXME: move all guid-related functions to a DatabaseVersionTracker class.
guidMutex()139 static RecursiveMutex& guidMutex()
140 {
141     AtomicallyInitializedStatic(RecursiveMutex&, mutex = *new RecursiveMutex);
142     return mutex;
143 }
144 
145 typedef HashMap<DatabaseGuid, String> GuidVersionMap;
guidToVersionMap()146 static GuidVersionMap& guidToVersionMap()
147 {
148     // Ensure the the mutex is locked.
149     ASSERT(guidMutex().locked());
150     DEFINE_STATIC_LOCAL(GuidVersionMap, map, ());
151     return map;
152 }
153 
154 // NOTE: Caller must lock guidMutex().
updateGuidVersionMap(DatabaseGuid guid,String newVersion)155 static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion)
156 {
157     // Ensure the the mutex is locked.
158     ASSERT(guidMutex().locked());
159 
160     // Note: It is not safe to put an empty string into the guidToVersionMap()
161     // map. That's because the map is cross-thread, but empty strings are
162     // per-thread. The copy() function makes a version of the string you can
163     // use on the current thread, but we need a string we can keep in a
164     // cross-thread data structure.
165     // FIXME: This is a quite-awkward restriction to have to program with.
166 
167     // Map null string to empty string (see comment above).
168     guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy());
169 }
170 
171 typedef HashMap<DatabaseGuid, HashSet<Database*>*> GuidDatabaseMap;
guidToDatabaseMap()172 static GuidDatabaseMap& guidToDatabaseMap()
173 {
174     // Ensure the the mutex is locked.
175     ASSERT(guidMutex().locked());
176     DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ());
177     return map;
178 }
179 
guidForOriginAndName(const String & origin,const String & name)180 static DatabaseGuid guidForOriginAndName(const String& origin, const String& name)
181 {
182     // Ensure the the mutex is locked.
183     ASSERT(guidMutex().locked());
184 
185     String stringID = origin + "/" + name;
186 
187     typedef HashMap<String, int> IDGuidMap;
188     DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ());
189     DatabaseGuid guid = stringIdentifierToGUIDMap.get(stringID);
190     if (!guid) {
191         static int currentNewGUID = 1;
192         guid = currentNewGUID++;
193         stringIdentifierToGUIDMap.set(stringID, guid);
194     }
195 
196     return guid;
197 }
198 
Database(DatabaseContext * databaseContext,const String & name,const String & expectedVersion,const String & displayName,unsigned long estimatedSize)199 Database::Database(DatabaseContext* databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
200     : m_databaseContext(databaseContext)
201     , m_name(name.isolatedCopy())
202     , m_expectedVersion(expectedVersion.isolatedCopy())
203     , m_displayName(displayName.isolatedCopy())
204     , m_estimatedSize(estimatedSize)
205     , m_guid(0)
206     , m_opened(false)
207     , m_new(false)
208     , m_transactionInProgress(false)
209     , m_isTransactionQueueEnabled(true)
210 {
211     m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy();
212 
213     m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName);
214 
215     if (m_name.isNull())
216         m_name = "";
217 
218     {
219         SafePointAwareMutexLocker locker(guidMutex());
220         m_guid = guidForOriginAndName(securityOrigin()->toString(), name);
221         HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
222         if (!hashSet) {
223             hashSet = new HashSet<Database*>;
224             guidToDatabaseMap().set(m_guid, hashSet);
225         }
226 
227         hashSet->add(this);
228     }
229 
230     m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name);
231 
232     m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
233     ASSERT(m_databaseContext->databaseThread());
234     ASSERT(m_databaseContext->isContextThread());
235 }
236 
~Database()237 Database::~Database()
238 {
239     // SQLite is "multi-thread safe", but each database handle can only be used
240     // on a single thread at a time.
241     //
242     // For Database, we open the SQLite database on the DatabaseThread, and
243     // hence we should also close it on that same thread. This means that the
244     // SQLite database need to be closed by another mechanism (see
245     // DatabaseContext::stopDatabases()). By the time we get here, the SQLite
246     // database should have already been closed.
247 
248     ASSERT(!m_opened);
249 }
250 
trace(Visitor * visitor)251 void Database::trace(Visitor* visitor)
252 {
253     visitor->trace(m_databaseContext);
254     visitor->trace(m_sqliteDatabase);
255     visitor->trace(m_databaseAuthorizer);
256     visitor->trace(m_transactionQueue);
257 }
258 
openAndVerifyVersion(bool setVersionInNewDatabase,DatabaseError & error,String & errorMessage)259 bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
260 {
261     TaskSynchronizer synchronizer;
262     if (!databaseContext()->databaseThreadAvailable())
263         return false;
264 
265     DatabaseTracker::tracker().prepareToOpenDatabase(this);
266     bool success = false;
267     OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
268     databaseContext()->databaseThread()->scheduleTask(task.release());
269     synchronizer.waitForTaskCompletion();
270 
271     return success;
272 }
273 
close()274 void Database::close()
275 {
276     ASSERT(databaseContext()->databaseThread());
277     ASSERT(databaseContext()->databaseThread()->isDatabaseThread());
278 
279     {
280         MutexLocker locker(m_transactionInProgressMutex);
281 
282         // Clean up transactions that have not been scheduled yet:
283         // Transaction phase 1 cleanup. See comment on "What happens if a
284         // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
285         RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = nullptr;
286         while (!m_transactionQueue.isEmpty()) {
287             transaction = m_transactionQueue.takeFirst();
288             transaction->notifyDatabaseThreadIsShuttingDown();
289         }
290 
291         m_isTransactionQueueEnabled = false;
292         m_transactionInProgress = false;
293     }
294 
295     closeDatabase();
296     databaseContext()->databaseThread()->recordDatabaseClosed(this);
297 }
298 
runTransaction(PassRefPtrWillBeRawPtr<SQLTransaction> transaction,bool readOnly,const ChangeVersionData * data)299 PassRefPtrWillBeRawPtr<SQLTransactionBackend> Database::runTransaction(PassRefPtrWillBeRawPtr<SQLTransaction> transaction,
300     bool readOnly, const ChangeVersionData* data)
301 {
302     MutexLocker locker(m_transactionInProgressMutex);
303     if (!m_isTransactionQueueEnabled)
304         return nullptr;
305 
306     RefPtrWillBeRawPtr<SQLTransactionWrapper> wrapper = nullptr;
307     if (data)
308         wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion());
309 
310     RefPtrWillBeRawPtr<SQLTransactionBackend> transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper.release(), readOnly);
311     m_transactionQueue.append(transactionBackend);
312     if (!m_transactionInProgress)
313         scheduleTransaction();
314 
315     return transactionBackend;
316 }
317 
inProgressTransactionCompleted()318 void Database::inProgressTransactionCompleted()
319 {
320     MutexLocker locker(m_transactionInProgressMutex);
321     m_transactionInProgress = false;
322     scheduleTransaction();
323 }
324 
scheduleTransaction()325 void Database::scheduleTransaction()
326 {
327     ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
328     RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = nullptr;
329 
330     if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty())
331         transaction = m_transactionQueue.takeFirst();
332 
333     if (transaction && databaseContext()->databaseThreadAvailable()) {
334         OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
335         WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
336         m_transactionInProgress = true;
337         databaseContext()->databaseThread()->scheduleTask(task.release());
338     } else {
339         m_transactionInProgress = false;
340     }
341 }
342 
scheduleTransactionStep(SQLTransactionBackend * transaction)343 void Database::scheduleTransactionStep(SQLTransactionBackend* transaction)
344 {
345     if (!databaseContext()->databaseThreadAvailable())
346         return;
347 
348     OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
349     WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
350     databaseContext()->databaseThread()->scheduleTask(task.release());
351 }
352 
transactionClient() const353 SQLTransactionClient* Database::transactionClient() const
354 {
355     return databaseContext()->databaseThread()->transactionClient();
356 }
357 
transactionCoordinator() const358 SQLTransactionCoordinator* Database::transactionCoordinator() const
359 {
360     return databaseContext()->databaseThread()->transactionCoordinator();
361 }
362 
363 // static
databaseInfoTableName()364 const char* Database::databaseInfoTableName()
365 {
366     return infoTableName;
367 }
368 
closeDatabase()369 void Database::closeDatabase()
370 {
371     if (!m_opened)
372         return;
373 
374     m_sqliteDatabase.close();
375     m_opened = false;
376     // See comment at the top this file regarding calling removeOpenDatabase().
377     DatabaseTracker::tracker().removeOpenDatabase(this);
378     {
379         SafePointAwareMutexLocker locker(guidMutex());
380 
381         HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
382         ASSERT(hashSet);
383         ASSERT(hashSet->contains(this));
384         hashSet->remove(this);
385         if (hashSet->isEmpty()) {
386             guidToDatabaseMap().remove(m_guid);
387             delete hashSet;
388             guidToVersionMap().remove(m_guid);
389         }
390     }
391 }
392 
version() const393 String Database::version() const
394 {
395     // Note: In multi-process browsers the cached value may be accurate, but we
396     // cannot read the actual version from the database without potentially
397     // inducing a deadlock.
398     // FIXME: Add an async version getter to the DatabaseAPI.
399     return getCachedVersion();
400 }
401 
402 class DoneCreatingDatabaseOnExitCaller {
403 public:
DoneCreatingDatabaseOnExitCaller(Database * database)404     DoneCreatingDatabaseOnExitCaller(Database* database)
405         : m_database(database)
406         , m_openSucceeded(false)
407     {
408     }
~DoneCreatingDatabaseOnExitCaller()409     ~DoneCreatingDatabaseOnExitCaller()
410     {
411         if (!m_openSucceeded)
412             DatabaseTracker::tracker().failedToOpenDatabase(m_database);
413     }
414 
setOpenSucceeded()415     void setOpenSucceeded() { m_openSucceeded = true; }
416 
417 private:
418     Database* m_database;
419     bool m_openSucceeded;
420 };
421 
performOpenAndVerify(bool shouldSetVersionInNewDatabase,DatabaseError & error,String & errorMessage)422 bool Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage)
423 {
424     DoneCreatingDatabaseOnExitCaller onExitCaller(this);
425     ASSERT(errorMessage.isEmpty());
426     ASSERT(error == DatabaseError::None); // Better not have any errors already.
427     // Presumed failure. We'll clear it if we succeed below.
428     error = DatabaseError::InvalidDatabaseState;
429 
430     const int maxSqliteBusyWaitTime = 30000;
431 
432     if (!m_sqliteDatabase.open(m_filename, true)) {
433         reportOpenDatabaseResult(1, InvalidStateError, m_sqliteDatabase.lastError());
434         errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
435         return false;
436     }
437     if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum())
438         WTF_LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
439 
440     m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
441 
442     String currentVersion;
443     {
444         SafePointAwareMutexLocker locker(guidMutex());
445 
446         GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid);
447         if (entry != guidToVersionMap().end()) {
448             // Map null string to empty string (see updateGuidVersionMap()).
449             currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy();
450             WTF_LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
451 
452             // Note: In multi-process browsers the cached value may be
453             // inaccurate, but we cannot read the actual version from the
454             // database without potentially inducing a form of deadlock, a
455             // busytimeout error when trying to access the database. So we'll
456             // use the cached value if we're unable to read the value from the
457             // database file without waiting.
458             // FIXME: Add an async openDatabase method to the DatabaseAPI.
459             const int noSqliteBusyWaitTime = 0;
460             m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime);
461             String versionFromDatabase;
462             if (getVersionFromDatabase(versionFromDatabase, false)) {
463                 currentVersion = versionFromDatabase;
464                 updateGuidVersionMap(m_guid, currentVersion);
465             }
466             m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
467         } else {
468             WTF_LOG(StorageAPI, "No cached version for guid %i", m_guid);
469 
470             SQLiteTransaction transaction(m_sqliteDatabase);
471             transaction.begin();
472             if (!transaction.inProgress()) {
473                 reportOpenDatabaseResult(2, InvalidStateError, m_sqliteDatabase.lastError());
474                 errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
475                 m_sqliteDatabase.close();
476                 return false;
477             }
478 
479             String tableName(infoTableName);
480             if (!m_sqliteDatabase.tableExists(tableName)) {
481                 m_new = true;
482 
483                 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);")) {
484                     reportOpenDatabaseResult(3, InvalidStateError, m_sqliteDatabase.lastError());
485                     errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
486                     transaction.rollback();
487                     m_sqliteDatabase.close();
488                     return false;
489                 }
490             } else if (!getVersionFromDatabase(currentVersion, false)) {
491                 reportOpenDatabaseResult(4, InvalidStateError, m_sqliteDatabase.lastError());
492                 errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
493                 transaction.rollback();
494                 m_sqliteDatabase.close();
495                 return false;
496             }
497 
498             if (currentVersion.length()) {
499                 WTF_LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
500             } else if (!m_new || shouldSetVersionInNewDatabase) {
501                 WTF_LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
502                 if (!setVersionInDatabase(m_expectedVersion, false)) {
503                     reportOpenDatabaseResult(5, InvalidStateError, m_sqliteDatabase.lastError());
504                     errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
505                     transaction.rollback();
506                     m_sqliteDatabase.close();
507                     return false;
508                 }
509                 currentVersion = m_expectedVersion;
510             }
511             updateGuidVersionMap(m_guid, currentVersion);
512             transaction.commit();
513         }
514     }
515 
516     if (currentVersion.isNull()) {
517         WTF_LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
518         currentVersion = "";
519     }
520 
521     // If the expected version isn't the empty string, ensure that the current
522     // database version we have matches that version. Otherwise, set an
523     // exception.
524     // If the expected version is the empty string, then we always return with
525     // whatever version of the database we have.
526     if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
527         reportOpenDatabaseResult(6, InvalidStateError, 0);
528         errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'";
529         m_sqliteDatabase.close();
530         return false;
531     }
532 
533     ASSERT(m_databaseAuthorizer);
534     m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get());
535 
536     // See comment at the top this file regarding calling addOpenDatabase().
537     DatabaseTracker::tracker().addOpenDatabase(this);
538     m_opened = true;
539 
540     // Declare success:
541     error = DatabaseError::None; // Clear the presumed error from above.
542     onExitCaller.setOpenSucceeded();
543 
544     if (m_new && !shouldSetVersionInNewDatabase) {
545         // The caller provided a creationCallback which will set the expected
546         // version.
547         m_expectedVersion = "";
548     }
549 
550     reportOpenDatabaseResult(0, -1, 0); // OK
551 
552     if (databaseContext()->databaseThread())
553         databaseContext()->databaseThread()->recordDatabaseOpen(this);
554     return true;
555 }
556 
stringIdentifier() const557 String Database::stringIdentifier() const
558 {
559     // Return a deep copy for ref counting thread safety
560     return m_name.isolatedCopy();
561 }
562 
displayName() const563 String Database::displayName() const
564 {
565     // Return a deep copy for ref counting thread safety
566     return m_displayName.isolatedCopy();
567 }
568 
estimatedSize() const569 unsigned long Database::estimatedSize() const
570 {
571     return m_estimatedSize;
572 }
573 
fileName() const574 String Database::fileName() const
575 {
576     // Return a deep copy for ref counting thread safety
577     return m_filename.isolatedCopy();
578 }
579 
getVersionFromDatabase(String & version,bool shouldCacheVersion)580 bool Database::getVersionFromDatabase(String& version, bool shouldCacheVersion)
581 {
582     String query(String("SELECT value FROM ") + infoTableName +  " WHERE key = '" + versionKey + "';");
583 
584     m_databaseAuthorizer->disable();
585 
586     bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version);
587     if (result) {
588         if (shouldCacheVersion)
589             setCachedVersion(version);
590     } else {
591         WTF_LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
592     }
593 
594     m_databaseAuthorizer->enable();
595 
596     return result;
597 }
598 
setVersionInDatabase(const String & version,bool shouldCacheVersion)599 bool Database::setVersionInDatabase(const String& version, bool shouldCacheVersion)
600 {
601     // The INSERT will replace an existing entry for the database with the new
602     // version number, due to the UNIQUE ON CONFLICT REPLACE clause in the
603     // CREATE statement (see Database::performOpenAndVerify()).
604     String query(String("INSERT INTO ") + infoTableName +  " (key, value) VALUES ('" + versionKey + "', ?);");
605 
606     m_databaseAuthorizer->disable();
607 
608     bool result = setTextValueInDatabase(m_sqliteDatabase, query, version);
609     if (result) {
610         if (shouldCacheVersion)
611             setCachedVersion(version);
612     } else {
613         WTF_LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data());
614     }
615 
616     m_databaseAuthorizer->enable();
617 
618     return result;
619 }
620 
setExpectedVersion(const String & version)621 void Database::setExpectedVersion(const String& version)
622 {
623     m_expectedVersion = version.isolatedCopy();
624 }
625 
getCachedVersion() const626 String Database::getCachedVersion() const
627 {
628     SafePointAwareMutexLocker locker(guidMutex());
629     return guidToVersionMap().get(m_guid).isolatedCopy();
630 }
631 
setCachedVersion(const String & actualVersion)632 void Database::setCachedVersion(const String& actualVersion)
633 {
634     // Update the in memory database version map.
635     SafePointAwareMutexLocker locker(guidMutex());
636     updateGuidVersionMap(m_guid, actualVersion);
637 }
638 
getActualVersionForTransaction(String & actualVersion)639 bool Database::getActualVersionForTransaction(String& actualVersion)
640 {
641     ASSERT(m_sqliteDatabase.transactionInProgress());
642     // Note: In multi-process browsers the cached value may be inaccurate. So we
643     // retrieve the value from the database and update the cached value here.
644     return getVersionFromDatabase(actualVersion, true);
645 }
646 
disableAuthorizer()647 void Database::disableAuthorizer()
648 {
649     ASSERT(m_databaseAuthorizer);
650     m_databaseAuthorizer->disable();
651 }
652 
enableAuthorizer()653 void Database::enableAuthorizer()
654 {
655     ASSERT(m_databaseAuthorizer);
656     m_databaseAuthorizer->enable();
657 }
658 
setAuthorizerPermissions(int permissions)659 void Database::setAuthorizerPermissions(int permissions)
660 {
661     ASSERT(m_databaseAuthorizer);
662     m_databaseAuthorizer->setPermissions(permissions);
663 }
664 
lastActionChangedDatabase()665 bool Database::lastActionChangedDatabase()
666 {
667     ASSERT(m_databaseAuthorizer);
668     return m_databaseAuthorizer->lastActionChangedDatabase();
669 }
670 
lastActionWasInsert()671 bool Database::lastActionWasInsert()
672 {
673     ASSERT(m_databaseAuthorizer);
674     return m_databaseAuthorizer->lastActionWasInsert();
675 }
676 
resetDeletes()677 void Database::resetDeletes()
678 {
679     ASSERT(m_databaseAuthorizer);
680     m_databaseAuthorizer->resetDeletes();
681 }
682 
hadDeletes()683 bool Database::hadDeletes()
684 {
685     ASSERT(m_databaseAuthorizer);
686     return m_databaseAuthorizer->hadDeletes();
687 }
688 
resetAuthorizer()689 void Database::resetAuthorizer()
690 {
691     if (m_databaseAuthorizer)
692         m_databaseAuthorizer->reset();
693 }
694 
maximumSize() const695 unsigned long long Database::maximumSize() const
696 {
697     return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
698 }
699 
incrementalVacuumIfNeeded()700 void Database::incrementalVacuumIfNeeded()
701 {
702     int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
703     int64_t totalSize = m_sqliteDatabase.totalSize();
704     if (totalSize <= 10 * freeSpaceSize) {
705         int result = m_sqliteDatabase.runIncrementalVacuumCommand();
706         reportVacuumDatabaseResult(result);
707         if (result != SQLResultOk)
708             logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg()));
709     }
710 }
711 
712 // These are used to generate histograms of errors seen with websql.
713 // See about:histograms in chromium.
reportOpenDatabaseResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)714 void Database::reportOpenDatabaseResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
715 {
716     if (Platform::current()->databaseObserver()) {
717         Platform::current()->databaseObserver()->reportOpenDatabaseResult(
718             createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
719             stringIdentifier(), false,
720             errorSite, webSqlErrorCode, sqliteErrorCode);
721     }
722 }
723 
reportChangeVersionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)724 void Database::reportChangeVersionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
725 {
726     if (Platform::current()->databaseObserver()) {
727         Platform::current()->databaseObserver()->reportChangeVersionResult(
728             createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
729             stringIdentifier(), false,
730             errorSite, webSqlErrorCode, sqliteErrorCode);
731     }
732 }
733 
reportStartTransactionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)734 void Database::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
735 {
736     if (Platform::current()->databaseObserver()) {
737         Platform::current()->databaseObserver()->reportStartTransactionResult(
738             createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
739             stringIdentifier(), false,
740             errorSite, webSqlErrorCode, sqliteErrorCode);
741     }
742 }
743 
reportCommitTransactionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)744 void Database::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
745 {
746     if (Platform::current()->databaseObserver()) {
747         Platform::current()->databaseObserver()->reportCommitTransactionResult(
748             createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
749             stringIdentifier(), false,
750             errorSite, webSqlErrorCode, sqliteErrorCode);
751     }
752 }
753 
reportExecuteStatementResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)754 void Database::reportExecuteStatementResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
755 {
756     if (Platform::current()->databaseObserver()) {
757         Platform::current()->databaseObserver()->reportExecuteStatementResult(
758             createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
759             stringIdentifier(), false,
760             errorSite, webSqlErrorCode, sqliteErrorCode);
761     }
762 }
763 
reportVacuumDatabaseResult(int sqliteErrorCode)764 void Database::reportVacuumDatabaseResult(int sqliteErrorCode)
765 {
766     if (Platform::current()->databaseObserver()) {
767         Platform::current()->databaseObserver()->reportVacuumDatabaseResult(
768             createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
769             stringIdentifier(), false, sqliteErrorCode);
770     }
771 }
772 
logErrorMessage(const String & message)773 void Database::logErrorMessage(const String& message)
774 {
775     executionContext()->addConsoleMessage(ConsoleMessage::create(StorageMessageSource, ErrorMessageLevel, message));
776 }
777 
executionContext() const778 ExecutionContext* Database::executionContext() const
779 {
780     return databaseContext()->executionContext();
781 }
782 
closeImmediately()783 void Database::closeImmediately()
784 {
785     ASSERT(executionContext()->isContextThread());
786     if (databaseContext()->databaseThreadAvailable() && opened()) {
787         logErrorMessage("forcibly closing database");
788         databaseContext()->databaseThread()->scheduleTask(DatabaseCloseTask::create(this, 0));
789     }
790 }
791 
changeVersion(const String & oldVersion,const String & newVersion,SQLTransactionCallback * callback,SQLTransactionErrorCallback * errorCallback,VoidCallback * successCallback)792 void Database::changeVersion(
793     const String& oldVersion,
794     const String& newVersion,
795     SQLTransactionCallback* callback,
796     SQLTransactionErrorCallback* errorCallback,
797     VoidCallback* successCallback)
798 {
799     ChangeVersionData data(oldVersion, newVersion);
800     runTransaction(callback, errorCallback, successCallback, false, &data);
801 }
802 
transaction(SQLTransactionCallback * callback,SQLTransactionErrorCallback * errorCallback,VoidCallback * successCallback)803 void Database::transaction(
804     SQLTransactionCallback* callback,
805     SQLTransactionErrorCallback* errorCallback,
806     VoidCallback* successCallback)
807 {
808     runTransaction(callback, errorCallback, successCallback, false);
809 }
810 
readTransaction(SQLTransactionCallback * callback,SQLTransactionErrorCallback * errorCallback,VoidCallback * successCallback)811 void Database::readTransaction(
812     SQLTransactionCallback* callback,
813     SQLTransactionErrorCallback* errorCallback,
814     VoidCallback* successCallback)
815 {
816     runTransaction(callback, errorCallback, successCallback, true);
817 }
818 
callTransactionErrorCallback(ExecutionContext *,SQLTransactionErrorCallback * callback,PassOwnPtr<SQLErrorData> errorData)819 static void callTransactionErrorCallback(ExecutionContext*, SQLTransactionErrorCallback* callback, PassOwnPtr<SQLErrorData> errorData)
820 {
821     RefPtrWillBeRawPtr<SQLError> error = SQLError::create(*errorData);
822     callback->handleEvent(error.get());
823 }
824 
runTransaction(SQLTransactionCallback * callback,SQLTransactionErrorCallback * errorCallback,VoidCallback * successCallback,bool readOnly,const ChangeVersionData * changeVersionData)825 void Database::runTransaction(
826     SQLTransactionCallback* callback,
827     SQLTransactionErrorCallback* errorCallback,
828     VoidCallback* successCallback,
829     bool readOnly,
830     const ChangeVersionData* changeVersionData)
831 {
832     // FIXME: Rather than passing errorCallback to SQLTransaction and then
833     // sometimes firing it ourselves, this code should probably be pushed down
834     // into Database so that we only create the SQLTransaction if we're
835     // actually going to run it.
836 #if ENABLE(ASSERT)
837     SQLTransactionErrorCallback* originalErrorCallback = errorCallback;
838 #endif
839     RefPtrWillBeRawPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, errorCallback, readOnly);
840     RefPtrWillBeRawPtr<SQLTransactionBackend> transactionBackend = runTransaction(transaction, readOnly, changeVersionData);
841     if (!transactionBackend) {
842         SQLTransactionErrorCallback* callback = transaction->releaseErrorCallback();
843         ASSERT(callback == originalErrorCallback);
844         if (callback) {
845             OwnPtr<SQLErrorData> error = SQLErrorData::create(SQLError::UNKNOWN_ERR, "database has been closed");
846             executionContext()->postTask(createCrossThreadTask(&callTransactionErrorCallback, callback, error.release()));
847         }
848     }
849 }
850 
851 // This object is constructed in a database thread, and destructed in the
852 // context thread.
853 class DeliverPendingCallbackTask FINAL : public ExecutionContextTask {
854 public:
create(PassRefPtrWillBeRawPtr<SQLTransaction> transaction)855     static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtrWillBeRawPtr<SQLTransaction> transaction)
856     {
857         return adoptPtr(new DeliverPendingCallbackTask(transaction));
858     }
859 
performTask(ExecutionContext *)860     virtual void performTask(ExecutionContext*) OVERRIDE
861     {
862         m_transaction->performPendingCallback();
863     }
864 
865 private:
DeliverPendingCallbackTask(PassRefPtrWillBeRawPtr<SQLTransaction> transaction)866     DeliverPendingCallbackTask(PassRefPtrWillBeRawPtr<SQLTransaction> transaction)
867         : m_transaction(transaction)
868     {
869     }
870 
871     RefPtrWillBeCrossThreadPersistent<SQLTransaction> m_transaction;
872 };
873 
scheduleTransactionCallback(SQLTransaction * transaction)874 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
875 {
876     executionContext()->postTask(DeliverPendingCallbackTask::create(transaction));
877 }
878 
performGetTableNames()879 Vector<String> Database::performGetTableNames()
880 {
881     disableAuthorizer();
882 
883     SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
884     if (statement.prepare() != SQLResultOk) {
885         WTF_LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
886         enableAuthorizer();
887         return Vector<String>();
888     }
889 
890     Vector<String> tableNames;
891     int result;
892     while ((result = statement.step()) == SQLResultRow) {
893         String name = statement.getColumnText(0);
894         if (name != databaseInfoTableName())
895             tableNames.append(name);
896     }
897 
898     enableAuthorizer();
899 
900     if (result != SQLResultDone) {
901         WTF_LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
902         return Vector<String>();
903     }
904 
905     return tableNames;
906 }
907 
tableNames()908 Vector<String> Database::tableNames()
909 {
910     // FIXME: Not using isolatedCopy on these strings looks ok since threads
911     // take strict turns in dealing with them. However, if the code changes,
912     // this may not be true anymore.
913     Vector<String> result;
914     TaskSynchronizer synchronizer;
915     if (!databaseContext()->databaseThreadAvailable())
916         return result;
917 
918     OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
919     databaseContext()->databaseThread()->scheduleTask(task.release());
920     synchronizer.waitForTaskCompletion();
921 
922     return result;
923 }
924 
securityOrigin() const925 SecurityOrigin* Database::securityOrigin() const
926 {
927     if (executionContext()->isContextThread())
928         return m_contextThreadSecurityOrigin.get();
929     if (databaseContext()->databaseThread()->isDatabaseThread())
930         return m_databaseThreadSecurityOrigin.get();
931     return 0;
932 }
933 
934 } // namespace blink
935