Changeset 62153 in webkit
- Timestamp:
- Jun 29, 2010 5:30:25 PM (14 years ago)
- Location:
- trunk
- Files:
-
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r62152 r62153 1 2010-06-29 Dumitru Daniliuc <dumi@chromium.org> 2 3 Reviewed by Darin Fisher. 4 5 Uncomment some test cases in open-database-sync-inputs, and 6 comment out for now the ones that fail in V8. 7 https://bugs.webkit.org/show_bug.cgi?id=40607 8 9 * fast/workers/storage/open-database-sync-inputs-expected.txt: 10 * fast/workers/storage/resources/open-database-sync-inputs.js: 11 (catch): 12 1 13 2010-06-29 François Sausset <sausset@gmail.com> 2 14 -
trunk/LayoutTests/fast/workers/storage/open-database-sync-inputs-expected.txt
r61866 r62153 6 6 PASS: undefined 7 7 PASS: TYPE_MISMATCH_ERR: DOM Exception 17 8 PASS: openDatabaseSync() succeeded. 9 PASS: openDatabaseSync() succeeded. 10 PASS: calling openDatabaseSync() with a creation callback succeeded. 8 11 -
trunk/LayoutTests/fast/workers/storage/resources/open-database-sync-inputs.js
r61866 r62153 35 35 try { 36 36 db = openDatabaseSync("DBName", "DBVersion", notAString, 1024); 37 postMessage("FAIL: the fourth argument to openDatabaseSync() must be an integer.");37 postMessage("FAIL: the third argument to openDatabaseSync() must be a string."); 38 38 } catch (err) { 39 39 postMessage("PASS: " + err.message); … … 47 47 } 48 48 49 // FIXME: Uncomment these tests when the sync DB API is implemented. 50 //try { 51 // db = openDatabaseSync("DBName", "DBVersion", "DBDescription", 1024); 52 // postMessage("PASS: openDatabaseSync() succeeded."); 53 //} catch (err) { 54 // postMessage("FAIL: " + err.message); 55 //} 56 // 57 //try { 58 // db = openDatabaseSync("DBName", "DBVersion", "DBDescription", 1024, function(db) { }); 59 // postMessage("PASS: calling openDatabaseSync() with a creation callback succeeded."); 60 //} catch (err) { 61 // postMessage("FAIL: " + err.message); 62 //} 49 try { 50 db = openDatabaseSync("DBName", "DBVersion", "DBDescription", 1024); 51 postMessage("PASS: openDatabaseSync() succeeded."); 52 } catch (err) { 53 postMessage("FAIL: " + err.message); 54 } 55 56 // Run this test case one more time, to test the code with an existing database. 57 try { 58 db = openDatabaseSync("DBName", "DBVersion", "DBDescription", 1024); 59 postMessage("PASS: openDatabaseSync() succeeded."); 60 } catch (err) { 61 postMessage("FAIL: " + err.message); 62 } 63 64 try { 65 // Need to create a new database, otherwise the creation callback won't be invoked. 66 db = openDatabaseSync("DBNameCreationCallback" + (new Date()).getTime(), "DBVersion", "DBDescription", 1024, 67 function(db) { 68 postMessage("PASS: calling openDatabaseSync() with a creation callback succeeded."); 69 }); 70 } catch (err) { 71 postMessage("FAIL: " + err.message); 72 } 63 73 64 74 postMessage("done"); -
trunk/WebCore/ChangeLog
r62152 r62153 1 2010-06-29 Dumitru Daniliuc <dumi@chromium.org> 2 3 Reviewed by Darin Fisher. 4 5 Implementing DatabaseSync::openDatabaseSync(). 6 https://bugs.webkit.org/show_bug.cgi?id=40607 7 8 1. Moved some common code from Database to AbstractDatabase. 9 2. Made performOpenAndVerify() virtual, since DatabaseSync doesn't 10 need to interact with DatabaseThread. 11 3. Removed the m_creationCallback field, since it's only needed in 12 the openDatabase{Sync} methods. 13 14 * storage/AbstractDatabase.cpp: 15 (WebCore::retrieveTextResultFromDatabase): 16 (WebCore::setTextValueInDatabase): 17 (WebCore::guidMutex): 18 (WebCore::guidToVersionMap): 19 (WebCore::updateGuidVersionMap): 20 (WebCore::guidToDatabaseMap): 21 (WebCore::guidForOriginAndName): 22 (WebCore::AbstractDatabase::databaseInfoTableName): 23 (WebCore::AbstractDatabase::AbstractDatabase): 24 (WebCore::AbstractDatabase::closeDatabase): 25 (WebCore::AbstractDatabase::version): 26 (WebCore::AbstractDatabase::performOpenAndVerify): 27 (WebCore::AbstractDatabase::scriptExecutionContext): 28 (WebCore::AbstractDatabase::securityOrigin): 29 (WebCore::AbstractDatabase::stringIdentifier): 30 (WebCore::AbstractDatabase::displayName): 31 (WebCore::AbstractDatabase::estimatedSize): 32 (WebCore::AbstractDatabase::fileName): 33 (WebCore::AbstractDatabase::databaseVersionKey): 34 (WebCore::AbstractDatabase::getVersionFromDatabase): 35 (WebCore::AbstractDatabase::setVersionInDatabase): 36 (WebCore::AbstractDatabase::versionMatchesExpected): 37 (WebCore::AbstractDatabase::setExpectedVersion): 38 (WebCore::AbstractDatabase::disableAuthorizer): 39 (WebCore::AbstractDatabase::enableAuthorizer): 40 (WebCore::AbstractDatabase::setAuthorizerReadOnly): 41 (WebCore::AbstractDatabase::lastActionChangedDatabase): 42 (WebCore::AbstractDatabase::lastActionWasInsert): 43 (WebCore::AbstractDatabase::resetDeletes): 44 (WebCore::AbstractDatabase::hadDeletes): 45 (WebCore::AbstractDatabase::resetAuthorizer): 46 * storage/AbstractDatabase.h: 47 (WebCore::AbstractDatabase::opened): 48 (WebCore::AbstractDatabase::isNew): 49 (WebCore::AbstractDatabase::databaseDebugName): 50 * storage/Database.cpp: 51 (WebCore::DatabaseCreationCallbackTask::create): 52 (WebCore::DatabaseCreationCallbackTask::performTask): 53 (WebCore::DatabaseCreationCallbackTask::DatabaseCreationCallbackTask): 54 (WebCore::Database::openDatabase): 55 (WebCore::Database::Database): 56 (WebCore::Database::version): 57 (WebCore::Database::openAndVerifyVersion): 58 (WebCore::Database::close): 59 (WebCore::Database::stop): 60 (WebCore::Database::performOpenAndVerify): 61 * storage/Database.h: 62 (WebCore::Database::sqliteDatabase): 63 * storage/DatabaseAuthorizer.cpp: 64 (WebCore::DatabaseAuthorizer::create): 65 (WebCore::DatabaseAuthorizer::DatabaseAuthorizer): 66 (WebCore::DatabaseAuthorizer::denyBasedOnTableName): 67 * storage/DatabaseAuthorizer.h: 68 * storage/DatabaseSync.cpp: 69 (WebCore::DatabaseSync::openDatabaseSync): 70 (WebCore::DatabaseSync::DatabaseSync): 71 (WebCore::DatabaseSync::~DatabaseSync): 72 (WebCore::DatabaseSync::markAsDeletedAndClose): 73 (WebCore::CloseSyncDatabaseOnContextThreadTask::create): 74 (WebCore::CloseSyncDatabaseOnContextThreadTask::performTask): 75 (WebCore::CloseSyncDatabaseOnContextThreadTask::CloseSyncDatabaseOnContextThreadTask): 76 (WebCore::DatabaseSync::closeImmediately): 77 * storage/DatabaseSync.h: 78 * storage/DatabaseTask.cpp: 79 (WebCore::DatabaseOpenTask::DatabaseOpenTask): 80 (WebCore::DatabaseOpenTask::doPerformTask): 81 * storage/DatabaseTask.h: 82 (WebCore::DatabaseOpenTask::create): 83 1 84 2010-06-29 François Sausset <sausset@gmail.com> 2 85 -
trunk/WebCore/storage/AbstractDatabase.cpp
r61866 r62153 31 31 32 32 #if ENABLE(DATABASE) 33 #include "DatabaseAuthorizer.h" 34 #include "DatabaseTracker.h" 35 #include "ExceptionCode.h" 36 #include "Logging.h" 37 #include "SQLiteStatement.h" 38 #include "ScriptExecutionContext.h" 39 #include "SecurityOrigin.h" 40 #include "StringHash.h" 41 #include <wtf/HashMap.h> 42 #include <wtf/HashSet.h> 43 #include <wtf/PassRefPtr.h> 44 #include <wtf/RefPtr.h> 45 #include <wtf/StdLibExtras.h> 33 46 34 47 namespace WebCore { 35 48 49 static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString) 50 { 51 SQLiteStatement statement(db, query); 52 int result = statement.prepare(); 53 54 if (result != SQLResultOk) { 55 LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data()); 56 return false; 57 } 58 59 result = statement.step(); 60 if (result == SQLResultRow) { 61 resultString = statement.getColumnText(0); 62 return true; 63 } 64 if (result == SQLResultDone) { 65 resultString = String(); 66 return true; 67 } 68 69 LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data()); 70 return false; 71 } 72 73 static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value) 74 { 75 SQLiteStatement statement(db, query); 76 int result = statement.prepare(); 77 78 if (result != SQLResultOk) { 79 LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data()); 80 return false; 81 } 82 83 statement.bindText(1, value); 84 85 result = statement.step(); 86 if (result != SQLResultDone) { 87 LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data()); 88 return false; 89 } 90 91 return true; 92 } 93 94 // FIXME: move all guid-related functions to a DatabaseVersionTracker class. 95 static Mutex& guidMutex() 96 { 97 // Note: We don't have to use AtomicallyInitializedStatic here because 98 // this function is called once in the constructor on the main thread 99 // before any other threads that call this function are used. 100 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 101 return mutex; 102 } 103 104 typedef HashMap<int, String> GuidVersionMap; 105 static GuidVersionMap& guidToVersionMap() 106 { 107 DEFINE_STATIC_LOCAL(GuidVersionMap, map, ()); 108 return map; 109 } 110 111 // NOTE: Caller must lock guidMutex(). 112 static inline void updateGuidVersionMap(int guid, String newVersion) 113 { 114 // Ensure the the mutex is locked. 115 ASSERT(!guidMutex().tryLock()); 116 117 // Note: It is not safe to put an empty string into the guidToVersionMap() map. 118 // That's because the map is cross-thread, but empty strings are per-thread. 119 // The copy() function makes a version of the string you can use on the current 120 // thread, but we need a string we can keep in a cross-thread data structure. 121 // FIXME: This is a quite-awkward restriction to have to program with. 122 123 // Map null string to empty string (see comment above). 124 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy()); 125 } 126 127 typedef HashMap<int, HashSet<AbstractDatabase*>*> GuidDatabaseMap; 128 static GuidDatabaseMap& guidToDatabaseMap() 129 { 130 DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ()); 131 return map; 132 } 133 134 static int guidForOriginAndName(const String& origin, const String& name) 135 { 136 String stringID = origin + "/" + name; 137 138 // Note: We don't have to use AtomicallyInitializedStatic here because 139 // this function is called once in the constructor on the main thread 140 // before any other threads that call this function are used. 141 DEFINE_STATIC_LOCAL(Mutex, stringIdentifierMutex, ()); 142 MutexLocker locker(stringIdentifierMutex); 143 typedef HashMap<String, int> IDGuidMap; 144 DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ()); 145 int guid = stringIdentifierToGUIDMap.get(stringID); 146 if (!guid) { 147 static int currentNewGUID = 1; 148 guid = currentNewGUID++; 149 stringIdentifierToGUIDMap.set(stringID, guid); 150 } 151 152 return guid; 153 } 154 36 155 static bool isDatabaseAvailable = true; 37 156 … … 46 165 } 47 166 167 // static 168 const String& AbstractDatabase::databaseInfoTableName() 169 { 170 DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__")); 171 return name; 172 } 173 174 AbstractDatabase::AbstractDatabase(ScriptExecutionContext* context, const String& name, const String& expectedVersion, 175 const String& displayName, unsigned long estimatedSize) 176 : m_scriptExecutionContext(context) 177 , m_name(name.crossThreadString()) 178 , m_expectedVersion(expectedVersion.crossThreadString()) 179 , m_displayName(displayName.crossThreadString()) 180 , m_estimatedSize(estimatedSize) 181 , m_guid(0) 182 , m_opened(false) 183 , m_new(false) 184 { 185 ASSERT(context->isContextThread()); 186 m_contextThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin(); 187 188 m_databaseAuthorizer = DatabaseAuthorizer::create(databaseInfoTableName()); 189 190 if (m_name.isNull()) 191 m_name = ""; 192 193 m_guid = guidForOriginAndName(securityOrigin()->toString(), name); 194 { 195 MutexLocker locker(guidMutex()); 196 197 HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); 198 if (!hashSet) { 199 hashSet = new HashSet<AbstractDatabase*>; 200 guidToDatabaseMap().set(m_guid, hashSet); 201 } 202 203 hashSet->add(this); 204 } 205 206 m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name); 207 DatabaseTracker::tracker().addOpenDatabase(this); 208 } 209 48 210 AbstractDatabase::~AbstractDatabase() 49 211 { 50 212 } 51 213 214 void AbstractDatabase::closeDatabase() 215 { 216 if (!m_opened) 217 return; 218 219 m_sqliteDatabase.close(); 220 m_opened = false; 221 { 222 MutexLocker locker(guidMutex()); 223 224 HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); 225 ASSERT(hashSet); 226 ASSERT(hashSet->contains(this)); 227 hashSet->remove(this); 228 if (hashSet->isEmpty()) { 229 guidToDatabaseMap().remove(m_guid); 230 delete hashSet; 231 guidToVersionMap().remove(m_guid); 232 } 233 } 234 } 235 236 String AbstractDatabase::version() const 237 { 238 MutexLocker locker(guidMutex()); 239 return guidToVersionMap().get(m_guid).threadsafeCopy(); 240 } 241 242 static const int maxSqliteBusyWaitTime = 30000; 243 bool AbstractDatabase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, ExceptionCode& ec) 244 { 245 if (!m_sqliteDatabase.open(m_filename, true)) { 246 LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data()); 247 ec = INVALID_STATE_ERR; 248 return false; 249 } 250 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) 251 LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m_filename.ascii().data()); 252 253 ASSERT(m_databaseAuthorizer); 254 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); 255 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); 256 257 String currentVersion; 258 { 259 MutexLocker locker(guidMutex()); 260 261 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); 262 if (entry != guidToVersionMap().end()) { 263 // Map null string to empty string (see updateGuidVersionMap()). 264 currentVersion = entry->second.isNull() ? String("") : entry->second; 265 LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); 266 } else { 267 LOG(StorageAPI, "No cached version for guid %i", m_guid); 268 269 if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { 270 m_new = true; 271 272 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { 273 LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data()); 274 ec = INVALID_STATE_ERR; 275 // Close the handle to the database file. 276 m_sqliteDatabase.close(); 277 return false; 278 } 279 } 280 281 if (!getVersionFromDatabase(currentVersion)) { 282 LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); 283 ec = INVALID_STATE_ERR; 284 // Close the handle to the database file. 285 m_sqliteDatabase.close(); 286 return false; 287 } 288 if (currentVersion.length()) { 289 LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); 290 } else if (!m_new || shouldSetVersionInNewDatabase) { 291 LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); 292 if (!setVersionInDatabase(m_expectedVersion)) { 293 LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); 294 ec = INVALID_STATE_ERR; 295 // Close the handle to the database file. 296 m_sqliteDatabase.close(); 297 return false; 298 } 299 currentVersion = m_expectedVersion; 300 } 301 302 updateGuidVersionMap(m_guid, currentVersion); 303 } 304 } 305 306 if (currentVersion.isNull()) { 307 LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); 308 currentVersion = ""; 309 } 310 311 // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. 312 // If the expected version is the empty string, then we always return with whatever version of the database we have. 313 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { 314 LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(), 315 databaseDebugName().ascii().data(), currentVersion.ascii().data()); 316 ec = INVALID_STATE_ERR; 317 // Close the handle to the database file. 318 m_sqliteDatabase.close(); 319 return false; 320 } 321 322 m_opened = true; 323 324 return true; 325 } 326 327 ScriptExecutionContext* AbstractDatabase::scriptExecutionContext() const 328 { 329 return m_scriptExecutionContext.get(); 330 } 331 332 SecurityOrigin* AbstractDatabase::securityOrigin() const 333 { 334 return m_contextThreadSecurityOrigin.get(); 335 } 336 337 String AbstractDatabase::stringIdentifier() const 338 { 339 // Return a deep copy for ref counting thread safety 340 return m_name.threadsafeCopy(); 341 } 342 343 String AbstractDatabase::displayName() const 344 { 345 // Return a deep copy for ref counting thread safety 346 return m_displayName.threadsafeCopy(); 347 } 348 349 unsigned long AbstractDatabase::estimatedSize() const 350 { 351 return m_estimatedSize; 352 } 353 354 String AbstractDatabase::fileName() const 355 { 356 // Return a deep copy for ref counting thread safety 357 return m_filename.threadsafeCopy(); 358 } 359 360 // static 361 const String& AbstractDatabase::databaseVersionKey() 362 { 363 DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey")); 364 return key; 365 } 366 367 bool AbstractDatabase::getVersionFromDatabase(String& version) 368 { 369 DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databaseInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';")); 370 371 m_databaseAuthorizer->disable(); 372 373 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.threadsafeCopy(), version); 374 if (!result) 375 LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); 376 377 m_databaseAuthorizer->enable(); 378 379 return result; 380 } 381 382 bool AbstractDatabase::setVersionInDatabase(const String& version) 383 { 384 // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE 385 // clause in the CREATE statement (see Database::performOpenAndVerify()). 386 DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);")); 387 388 m_databaseAuthorizer->disable(); 389 390 bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threadsafeCopy(), version); 391 if (!result) 392 LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data()); 393 394 m_databaseAuthorizer->enable(); 395 396 return result; 397 } 398 399 bool AbstractDatabase::versionMatchesExpected() const 400 { 401 if (!m_expectedVersion.isEmpty()) { 402 MutexLocker locker(guidMutex()); 403 return m_expectedVersion == guidToVersionMap().get(m_guid); 404 } 405 406 return true; 407 } 408 409 void AbstractDatabase::setExpectedVersion(const String& version) 410 { 411 m_expectedVersion = version.threadsafeCopy(); 412 // Update the in memory database version map. 413 MutexLocker locker(guidMutex()); 414 updateGuidVersionMap(m_guid, version); 415 } 416 417 void AbstractDatabase::disableAuthorizer() 418 { 419 ASSERT(m_databaseAuthorizer); 420 m_databaseAuthorizer->disable(); 421 } 422 423 void AbstractDatabase::enableAuthorizer() 424 { 425 ASSERT(m_databaseAuthorizer); 426 m_databaseAuthorizer->enable(); 427 } 428 429 void AbstractDatabase::setAuthorizerReadOnly() 430 { 431 ASSERT(m_databaseAuthorizer); 432 m_databaseAuthorizer->setReadOnly(); 433 } 434 435 bool AbstractDatabase::lastActionChangedDatabase() 436 { 437 ASSERT(m_databaseAuthorizer); 438 return m_databaseAuthorizer->lastActionChangedDatabase(); 439 } 440 441 bool AbstractDatabase::lastActionWasInsert() 442 { 443 ASSERT(m_databaseAuthorizer); 444 return m_databaseAuthorizer->lastActionWasInsert(); 445 } 446 447 void AbstractDatabase::resetDeletes() 448 { 449 ASSERT(m_databaseAuthorizer); 450 m_databaseAuthorizer->resetDeletes(); 451 } 452 453 bool AbstractDatabase::hadDeletes() 454 { 455 ASSERT(m_databaseAuthorizer); 456 return m_databaseAuthorizer->hadDeletes(); 457 } 458 459 void AbstractDatabase::resetAuthorizer() 460 { 461 if (m_databaseAuthorizer) 462 m_databaseAuthorizer->reset(); 463 } 464 52 465 } // namespace WebCore 53 466 -
trunk/WebCore/storage/AbstractDatabase.h
r61866 r62153 33 33 34 34 #include "PlatformString.h" 35 #include "SQLiteDatabase.h" 36 #include <wtf/Forward.h> 35 37 #include <wtf/ThreadSafeShared.h> 38 #ifndef NDEBUG 39 #include "SecurityOrigin.h" 40 #endif 36 41 37 42 namespace WebCore { 38 43 44 class DatabaseAuthorizer; 39 45 class ScriptExecutionContext; 40 46 class SecurityOrigin; 47 48 typedef int ExceptionCode; 41 49 42 50 class AbstractDatabase : public ThreadSafeShared<AbstractDatabase> { … … 47 55 virtual ~AbstractDatabase(); 48 56 49 virtual ScriptExecutionContext* scriptExecutionContext() const = 0; 50 virtual SecurityOrigin* securityOrigin() const = 0; 51 virtual String stringIdentifier() const = 0; 52 virtual String displayName() const = 0; 53 virtual unsigned long estimatedSize() const = 0; 54 virtual String fileName() const = 0; 57 virtual String version() const; 58 59 bool opened() const { return m_opened; } 60 bool isNew() const { return m_new; } 61 62 virtual ScriptExecutionContext* scriptExecutionContext() const; 63 virtual SecurityOrigin* securityOrigin() const; 64 virtual String stringIdentifier() const; 65 virtual String displayName() const; 66 virtual unsigned long estimatedSize() const; 67 virtual String fileName() const; 68 69 // FIXME: move all version-related methods to a DatabaseVersionTracker class 70 bool versionMatchesExpected() const; 71 void setExpectedVersion(const String& version); 72 bool getVersionFromDatabase(String& version); 73 bool setVersionInDatabase(const String& version); 74 75 void disableAuthorizer(); 76 void enableAuthorizer(); 77 void setAuthorizerReadOnly(); 78 bool lastActionChangedDatabase(); 79 bool lastActionWasInsert(); 80 void resetDeletes(); 81 bool hadDeletes(); 82 void resetAuthorizer(); 55 83 56 84 virtual void markAsDeletedAndClose() = 0; 57 85 virtual void closeImmediately() = 0; 86 87 protected: 88 AbstractDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, 89 const String& displayName, unsigned long estimatedSize); 90 91 void closeDatabase(); 92 93 virtual bool performOpenAndVerify(bool shouldSetVersionInNewDatabase, ExceptionCode& ec); 94 95 static const String& databaseInfoTableName(); 96 97 RefPtr<ScriptExecutionContext> m_scriptExecutionContext; 98 RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin; 99 100 String m_name; 101 String m_expectedVersion; 102 String m_displayName; 103 unsigned long m_estimatedSize; 104 String m_filename; 105 106 SQLiteDatabase m_sqliteDatabase; 107 108 #ifndef NDEBUG 109 String databaseDebugName() const { return m_contextThreadSecurityOrigin->toString() + "::" + m_name; } 110 #endif 111 112 private: 113 static const String& databaseVersionKey(); 114 115 int m_guid; 116 bool m_opened; 117 bool m_new; 118 119 RefPtr<DatabaseAuthorizer> m_databaseAuthorizer; 58 120 }; 59 121 -
trunk/WebCore/storage/Database.cpp
r61866 r62153 32 32 #if ENABLE(DATABASE) 33 33 #include "ChangeVersionWrapper.h" 34 #include "DatabaseAuthorizer.h"35 34 #include "DatabaseCallback.h" 36 35 #include "DatabaseTask.h" … … 51 50 #include "ScriptExecutionContext.h" 52 51 #include "SecurityOrigin.h" 53 #include "StringHash.h"54 52 #include "VoidCallback.h" 55 53 #include <wtf/OwnPtr.h> … … 65 63 namespace WebCore { 66 64 67 // If we sleep for more the 30 seconds while blocked on SQLITE_BUSY, give up.68 static const int maxSqliteBusyWaitTime = 30000;69 70 const String& Database::databaseInfoTableName()71 {72 DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__"));73 return name;74 }75 76 static Mutex& guidMutex()77 {78 // Note: We don't have to use AtomicallyInitializedStatic here because79 // this function is called once in the constructor on the main thread80 // before any other threads that call this function are used.81 DEFINE_STATIC_LOCAL(Mutex, mutex, ());82 return mutex;83 }84 85 typedef HashMap<int, String> GuidVersionMap;86 static GuidVersionMap& guidToVersionMap()87 {88 DEFINE_STATIC_LOCAL(GuidVersionMap, map, ());89 return map;90 }91 92 // NOTE: Caller must lock guidMutex().93 static inline void updateGuidVersionMap(int guid, String newVersion)94 {95 // Ensure the the mutex is locked.96 ASSERT(!guidMutex().tryLock());97 98 // Note: It is not safe to put an empty string into the guidToVersionMap() map.99 // That's because the map is cross-thread, but empty strings are per-thread.100 // The copy() function makes a version of the string you can use on the current101 // thread, but we need a string we can keep in a cross-thread data structure.102 // FIXME: This is a quite-awkward restriction to have to program with.103 104 // Map null string to empty string (see comment above).105 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy());106 }107 108 typedef HashMap<int, HashSet<Database*>*> GuidDatabaseMap;109 static GuidDatabaseMap& guidToDatabaseMap()110 {111 DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ());112 return map;113 }114 115 static const String& databaseVersionKey()116 {117 DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey"));118 return key;119 }120 121 static int guidForOriginAndName(const String& origin, const String& name);122 123 65 class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task { 124 66 public: 125 static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database )126 { 127 return new DatabaseCreationCallbackTask(database );128 } 129 130 virtual void performTask(ScriptExecutionContext* )131 { 132 m_ database->performCreationCallback();67 static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback) 68 { 69 return new DatabaseCreationCallbackTask(database, creationCallback); 70 } 71 72 virtual void performTask(ScriptExecutionContext* context) 73 { 74 m_creationCallback->handleEvent(context, m_database.get()); 133 75 } 134 76 135 77 private: 136 DatabaseCreationCallbackTask(PassRefPtr<Database> database )78 DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback) 137 79 : m_database(database) 80 , m_creationCallback(callback) 138 81 { 139 82 } 140 83 141 84 RefPtr<Database> m_database; 85 RefPtr<DatabaseCallback> m_creationCallback; 142 86 }; 143 87 … … 148 92 { 149 93 if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) { 150 // FIXME: There should be an exception raised here in addition to returning a null Database object. The question has been raised with the WHATWG.151 94 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data()); 152 95 return 0; 153 96 } 154 97 155 RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize , creationCallback));156 157 if (!database->openAndVerifyVersion( e)) {98 RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize)); 99 100 if (!database->openAndVerifyVersion(!creationCallback, e)) { 158 101 LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); 159 102 context->removeOpenDatabase(database.get()); … … 177 120 // implementation issues, we have to reset m_expectedVersion here instead of doing 178 121 // it inside performOpenAndVerify() which is run on the DB thread. 179 if (database->isNew() && database->m_creationCallback.get()) {122 if (database->isNew() && creationCallback.get()) { 180 123 database->m_expectedVersion = ""; 181 124 LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get()); 182 database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database ));125 database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback)); 183 126 } 184 127 … … 186 129 } 187 130 188 Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback) 189 : m_transactionInProgress(false) 131 Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) 132 : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize) 133 , m_transactionInProgress(false) 190 134 , m_isTransactionQueueEnabled(true) 191 , m_scriptExecutionContext(context)192 , m_name(name.crossThreadString())193 , m_guid(0)194 , m_expectedVersion(expectedVersion.crossThreadString())195 , m_displayName(displayName.crossThreadString())196 , m_estimatedSize(estimatedSize)197 135 , m_deleted(false) 198 136 , m_stopped(false) 199 , m_opened(false) 200 , m_new(false) 201 , m_creationCallback(creationCallback) 202 { 203 ASSERT(m_scriptExecutionContext.get()); 204 m_contextThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin(); 137 { 205 138 m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->threadsafeCopy(); 206 if (m_name.isNull())207 m_name = "";208 139 209 140 ScriptController::initializeThreading(); 210 211 m_guid = guidForOriginAndName(securityOrigin()->toString(), name);212 213 {214 MutexLocker locker(guidMutex());215 216 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);217 if (!hashSet) {218 hashSet = new HashSet<Database*>;219 guidToDatabaseMap().set(m_guid, hashSet);220 }221 222 hashSet->add(this);223 }224 225 141 ASSERT(m_scriptExecutionContext->databaseThread()); 226 m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name); 227 228 DatabaseTracker::tracker().addOpenDatabase(this); 229 context->addOpenDatabase(this); 142 143 m_scriptExecutionContext->addOpenDatabase(this); 230 144 } 231 145 … … 254 168 } 255 169 256 bool Database::openAndVerifyVersion(ExceptionCode& e) 170 String Database::version() const 171 { 172 if (m_deleted) 173 return String(); 174 return AbstractDatabase::version(); 175 } 176 177 bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode& e) 257 178 { 258 179 if (!m_scriptExecutionContext->databaseThread()) 259 180 return false; 260 m_databaseAuthorizer = DatabaseAuthorizer::create();261 181 262 182 bool success = false; 263 183 DatabaseTaskSynchronizer synchronizer; 264 OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, &synchronizer, e, success);184 OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, success); 265 185 266 186 m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); … … 268 188 269 189 return success; 270 }271 272 273 static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)274 {275 SQLiteStatement statement(db, query);276 int result = statement.prepare();277 278 if (result != SQLResultOk) {279 LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());280 return false;281 }282 283 result = statement.step();284 if (result == SQLResultRow) {285 resultString = statement.getColumnText(0);286 return true;287 } else if (result == SQLResultDone) {288 resultString = String();289 return true;290 } else {291 LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());292 return false;293 }294 }295 296 bool Database::getVersionFromDatabase(String& version)297 {298 DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databaseInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';"));299 300 m_databaseAuthorizer->disable();301 302 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.threadsafeCopy(), version);303 if (!result)304 LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());305 306 m_databaseAuthorizer->enable();307 308 return result;309 }310 311 static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)312 {313 SQLiteStatement statement(db, query);314 int result = statement.prepare();315 316 if (result != SQLResultOk) {317 LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());318 return false;319 }320 321 statement.bindText(1, value);322 323 result = statement.step();324 if (result != SQLResultDone) {325 LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());326 return false;327 }328 329 return true;330 }331 332 bool Database::setVersionInDatabase(const String& version)333 {334 // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE335 // clause in the CREATE statement (see Database::performOpenAndVerify()).336 DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);"));337 338 m_databaseAuthorizer->disable();339 340 bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threadsafeCopy(), version);341 if (!result)342 LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data());343 344 m_databaseAuthorizer->enable();345 346 return result;347 }348 349 bool Database::versionMatchesExpected() const350 {351 if (!m_expectedVersion.isEmpty()) {352 MutexLocker locker(guidMutex());353 return m_expectedVersion == guidToVersionMap().get(m_guid);354 }355 356 return true;357 190 } 358 191 … … 411 244 RefPtr<Database> protect = this; 412 245 413 if (!m_opened)414 return;415 416 246 ASSERT(m_scriptExecutionContext->databaseThread()); 417 247 ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()); 418 m_sqliteDatabase.close(); 248 249 closeDatabase(); 250 419 251 // Must ref() before calling databaseThread()->recordDatabaseClosed(). 420 252 m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this); 421 m_opened = false;422 423 {424 MutexLocker locker(guidMutex());425 426 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);427 ASSERT(hashSet);428 ASSERT(hashSet->contains(this));429 hashSet->remove(this);430 if (hashSet->isEmpty()) {431 guidToDatabaseMap().remove(m_guid);432 delete hashSet;433 guidToVersionMap().remove(m_guid);434 }435 }436 437 253 m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this); 438 254 if (policy == RemoveDatabaseFromContext) … … 464 280 m_transactionInProgress = false; 465 281 } 282 283 if (m_scriptExecutionContext->databaseThread()) 284 m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this); 466 285 } 467 286 … … 469 288 { 470 289 return DatabaseTracker::tracker().getMaxSizeForDatabase(this); 471 }472 473 void Database::disableAuthorizer()474 {475 ASSERT(m_databaseAuthorizer);476 m_databaseAuthorizer->disable();477 }478 479 void Database::enableAuthorizer()480 {481 ASSERT(m_databaseAuthorizer);482 m_databaseAuthorizer->enable();483 }484 485 void Database::setAuthorizerReadOnly()486 {487 ASSERT(m_databaseAuthorizer);488 m_databaseAuthorizer->setReadOnly();489 }490 491 bool Database::lastActionChangedDatabase()492 {493 ASSERT(m_databaseAuthorizer);494 return m_databaseAuthorizer->lastActionChangedDatabase();495 }496 497 bool Database::lastActionWasInsert()498 {499 ASSERT(m_databaseAuthorizer);500 return m_databaseAuthorizer->lastActionWasInsert();501 }502 503 void Database::resetDeletes()504 {505 ASSERT(m_databaseAuthorizer);506 m_databaseAuthorizer->resetDeletes();507 }508 509 bool Database::hadDeletes()510 {511 ASSERT(m_databaseAuthorizer);512 return m_databaseAuthorizer->hadDeletes();513 }514 515 static int guidForOriginAndName(const String& origin, const String& name)516 {517 String stringID;518 if (origin.endsWith("/"))519 stringID = origin + name;520 else521 stringID = origin + "/" + name;522 523 // Note: We don't have to use AtomicallyInitializedStatic here because524 // this function is called once in the constructor on the main thread525 // before any other threads that call this function are used.526 DEFINE_STATIC_LOCAL(Mutex, stringIdentifierMutex, ());527 MutexLocker locker(stringIdentifierMutex);528 typedef HashMap<String, int> IDGuidMap;529 DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ());530 int guid = stringIdentifierToGUIDMap.get(stringID);531 if (!guid) {532 static int currentNewGUID = 1;533 guid = currentNewGUID++;534 stringIdentifierToGUIDMap.set(stringID, guid);535 }536 537 return guid;538 }539 540 void Database::resetAuthorizer()541 {542 if (m_databaseAuthorizer)543 m_databaseAuthorizer->reset();544 290 } 545 291 … … 558 304 } 559 305 560 bool Database::performOpenAndVerify(ExceptionCode& e) 561 { 562 if (!m_sqliteDatabase.open(m_filename, true)) { 563 LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data()); 564 e = INVALID_STATE_ERR; 565 return false; 566 } 567 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) 568 LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m_filename.ascii().data()); 569 570 ASSERT(m_databaseAuthorizer); 571 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); 572 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); 573 574 String currentVersion; 575 { 576 MutexLocker locker(guidMutex()); 577 578 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); 579 if (entry != guidToVersionMap().end()) { 580 // Map null string to empty string (see updateGuidVersionMap()). 581 currentVersion = entry->second.isNull() ? String("") : entry->second; 582 LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); 583 } else { 584 LOG(StorageAPI, "No cached version for guid %i", m_guid); 585 586 if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { 587 m_new = true; 588 589 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { 590 LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data()); 591 e = INVALID_STATE_ERR; 592 // Close the handle to the database file. 593 m_sqliteDatabase.close(); 594 return false; 595 } 596 } 597 598 if (!getVersionFromDatabase(currentVersion)) { 599 LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); 600 e = INVALID_STATE_ERR; 601 // Close the handle to the database file. 602 m_sqliteDatabase.close(); 603 return false; 604 } 605 if (currentVersion.length()) { 606 LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); 607 } else if (!m_new || !m_creationCallback) { 608 LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); 609 if (!setVersionInDatabase(m_expectedVersion)) { 610 LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); 611 e = INVALID_STATE_ERR; 612 // Close the handle to the database file. 613 m_sqliteDatabase.close(); 614 return false; 615 } 616 currentVersion = m_expectedVersion; 617 } 618 619 updateGuidVersionMap(m_guid, currentVersion); 620 } 621 } 622 623 if (currentVersion.isNull()) { 624 LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); 625 currentVersion = ""; 626 } 627 628 // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. 629 // If the expected version is the empty string, then we always return with whatever version of the database we have. 630 if ((!m_new || !m_creationCallback) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { 631 LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(), 632 databaseDebugName().ascii().data(), currentVersion.ascii().data()); 633 e = INVALID_STATE_ERR; 634 // Close the handle to the database file. 635 m_sqliteDatabase.close(); 636 return false; 637 } 638 639 // All checks passed and we still have a handle to this database file. 640 // Make sure DatabaseThread closes it when DatabaseThread goes away. 641 m_opened = true; 642 if (m_scriptExecutionContext->databaseThread()) 643 m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this); 644 645 return true; 306 bool Database::performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode& e) 307 { 308 if (AbstractDatabase::performOpenAndVerify(setVersionInNewDatabase, e)) { 309 if (m_scriptExecutionContext->databaseThread()) 310 m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this); 311 312 return true; 313 } 314 315 return false; 646 316 } 647 317 … … 758 428 } 759 429 760 void Database::performCreationCallback()761 {762 m_creationCallback->handleEvent(m_scriptExecutionContext.get(), this);763 }764 765 430 SQLTransactionClient* Database::transactionClient() const 766 431 { … … 771 436 { 772 437 return m_scriptExecutionContext->databaseThread()->transactionCoordinator(); 773 }774 775 String Database::version() const776 {777 if (m_deleted)778 return String();779 MutexLocker locker(guidMutex());780 return guidToVersionMap().get(m_guid).threadsafeCopy();781 438 } 782 439 … … 798 455 } 799 456 800 void Database::setExpectedVersion(const String& version)801 {802 m_expectedVersion = version.threadsafeCopy();803 // Update the in memory database version map.804 MutexLocker locker(guidMutex());805 updateGuidVersionMap(m_guid, version);806 }807 808 457 SecurityOrigin* Database::securityOrigin() const 809 458 { … … 815 464 } 816 465 817 String Database::stringIdentifier() const818 {819 // Return a deep copy for ref counting thread safety820 return m_name.threadsafeCopy();821 }822 823 String Database::displayName() const824 {825 // Return a deep copy for ref counting thread safety826 return m_displayName.threadsafeCopy();827 }828 829 unsigned long Database::estimatedSize() const830 {831 return m_estimatedSize;832 }833 834 String Database::fileName() const835 {836 // Return a deep copy for ref counting thread safety837 return m_filename.threadsafeCopy();838 }839 840 466 void Database::incrementalVacuumIfNeeded() 841 467 { -
trunk/WebCore/storage/Database.h
r61866 r62153 34 34 #include "PlatformString.h" 35 35 #include "SQLiteDatabase.h" 36 #ifndef NDEBUG37 #include "SecurityOrigin.h"38 #endif39 36 40 37 #include <wtf/Deque.h> … … 43 40 namespace WebCore { 44 41 45 class DatabaseAuthorizer;46 42 class DatabaseCallback; 47 class DatabaseThread;48 43 class ScriptExecutionContext; 49 44 class SecurityOrigin; … … 55 50 class VoidCallback; 56 51 57 typedef int ExceptionCode;58 59 52 class Database : public AbstractDatabase { 60 53 public: … … 64 57 static PassRefPtr<Database> openDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, 65 58 unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&); 66 String version() const;59 virtual String version() const; 67 60 void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback>, 68 61 PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); … … 71 64 72 65 // Internal engine support 73 static const String& databaseInfoTableName();74 75 void disableAuthorizer();76 void enableAuthorizer();77 void setAuthorizerReadOnly();78 bool lastActionChangedDatabase();79 bool lastActionWasInsert();80 void resetDeletes();81 bool hadDeletes();82 83 66 Vector<String> tableNames(); 84 67 85 virtual ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext.get(); }86 68 virtual SecurityOrigin* securityOrigin() const; 87 69 SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; } 88 virtual String stringIdentifier() const;89 virtual String displayName() const;90 virtual unsigned long estimatedSize() const;91 virtual String fileName() const;92 93 bool getVersionFromDatabase(String&);94 bool setVersionInDatabase(const String&);95 void setExpectedVersion(const String&);96 bool versionMatchesExpected() const;97 70 98 71 virtual void markAsDeletedAndClose(); … … 102 75 void close(ClosePolicy); 103 76 virtual void closeImmediately(); 104 bool opened() const { return m_opened; }105 77 106 78 void stop(); 107 79 bool stopped() const { return m_stopped; } 108 109 bool isNew() const { return m_new; }110 80 111 81 unsigned long long databaseSize() const; … … 113 83 114 84 // Called from DatabaseThread, must be prepared to work on the background thread 115 void resetAuthorizer();116 85 void performPolicyChecks(); 117 86 118 bool performOpenAndVerify(ExceptionCode&);87 virtual bool performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode&); 119 88 120 89 void inProgressTransactionCompleted(); … … 123 92 124 93 Vector<String> performGetTableNames(); 125 void performCreationCallback();126 94 127 95 SQLTransactionClient* transactionClient() const; … … 132 100 private: 133 101 Database(ScriptExecutionContext*, const String& name, const String& expectedVersion, 134 const String& displayName, unsigned long estimatedSize , PassRefPtr<DatabaseCallback>);102 const String& displayName, unsigned long estimatedSize); 135 103 136 bool openAndVerifyVersion( ExceptionCode&);104 bool openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode&); 137 105 138 106 void scheduleTransaction(); 107 108 static void deliverPendingCallback(void*); 139 109 140 110 Deque<RefPtr<SQLTransaction> > m_transactionQueue; … … 143 113 bool m_isTransactionQueueEnabled; 144 114 145 static void deliverPendingCallback(void*);146 147 RefPtr<ScriptExecutionContext> m_scriptExecutionContext;148 RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin;149 115 RefPtr<SecurityOrigin> m_databaseThreadSecurityOrigin; 150 String m_name;151 int m_guid;152 String m_expectedVersion;153 String m_displayName;154 unsigned long m_estimatedSize;155 String m_filename;156 116 157 117 bool m_deleted; 158 159 118 bool m_stopped; 160 161 bool m_opened;162 163 bool m_new;164 165 SQLiteDatabase m_sqliteDatabase;166 RefPtr<DatabaseAuthorizer> m_databaseAuthorizer;167 168 RefPtr<DatabaseCallback> m_creationCallback;169 170 #ifndef NDEBUG171 String databaseDebugName() const { return m_contextThreadSecurityOrigin->toString() + "::" + m_name; }172 #endif173 119 }; 174 120 -
trunk/WebCore/storage/DatabaseAuthorizer.cpp
r61866 r62153 31 31 32 32 #if ENABLE(DATABASE) 33 #include "Database.h"34 33 #include "PlatformString.h" 34 #include <wtf/PassRefPtr.h> 35 35 36 36 namespace WebCore { 37 37 38 DatabaseAuthorizer::DatabaseAuthorizer() 38 PassRefPtr<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName) 39 { 40 return adoptRef(new DatabaseAuthorizer(databaseInfoTableName)); 41 } 42 43 DatabaseAuthorizer::DatabaseAuthorizer(const String& databaseInfoTableName) 39 44 : m_securityEnabled(false) 45 , m_databaseInfoTableName(databaseInfoTableName) 40 46 { 41 47 reset(); … … 389 395 } 390 396 391 int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) 397 int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const 392 398 { 393 399 if (!m_securityEnabled) … … 400 406 // return SQLAuthDeny; 401 407 402 if (equalIgnoringCase(tableName, Database::databaseInfoTableName()))408 if (equalIgnoringCase(tableName, m_databaseInfoTableName)) 403 409 return SQLAuthDeny; 404 410 -
trunk/WebCore/storage/DatabaseAuthorizer.h
r61866 r62153 29 29 #define DatabaseAuthorizer_h 30 30 31 #include "PlatformString.h" 31 32 #include "StringHash.h" 33 #include <wtf/Forward.h> 32 34 #include <wtf/HashSet.h> 33 #include <wtf/PassRefPtr.h> 34 #include <wtf/Threading.h> 35 #include <wtf/ThreadSafeShared.h> 35 36 36 37 namespace WebCore { … … 44 45 class DatabaseAuthorizer : public ThreadSafeShared<DatabaseAuthorizer> { 45 46 public: 46 static PassRefPtr<DatabaseAuthorizer> create( ) { return adoptRef(new DatabaseAuthorizer); }47 static PassRefPtr<DatabaseAuthorizer> create(const String& databaseInfoTableName); 47 48 48 49 int createTable(const String& tableName); … … 98 99 99 100 private: 100 DatabaseAuthorizer( );101 DatabaseAuthorizer(const String& databaseInfoTableName); 101 102 void addWhitelistedFunctions(); 102 int denyBasedOnTableName(const String&) ;103 int denyBasedOnTableName(const String&) const; 103 104 int updateDeletesBasedOnTableName(const String&); 104 105 … … 109 110 bool m_hadDeletes : 1; 110 111 112 const String m_databaseInfoTableName; 113 111 114 HashSet<String, CaseFoldingHash> m_whitelistedFunctions; 112 115 }; -
trunk/WebCore/storage/DatabaseSync.cpp
r61866 r62153 32 32 #if ENABLE(DATABASE) 33 33 #include "DatabaseCallback.h" 34 #include "DatabaseTracker.h" 34 35 #include "ExceptionCode.h" 36 #include "Logging.h" 35 37 #include "SQLTransactionSyncCallback.h" 36 38 #include "ScriptExecutionContext.h" 39 #include "SecurityOrigin.h" 37 40 #include <wtf/PassRefPtr.h> 38 41 #include <wtf/RefPtr.h> 39 #include <wtf/StdLibExtras.h>40 42 41 43 namespace WebCore { 42 44 43 const String& DatabaseSync::databaseInfoTableName() 45 PassRefPtr<DatabaseSync> DatabaseSync::openDatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, 46 unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec) 44 47 { 45 DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__")); 46 return name; 47 } 48 ASSERT(context->isContextThread()); 48 49 49 PassRefPtr<DatabaseSync> DatabaseSync::openDatabaseSync(ScriptExecutionContext*, const String&, const String&, const String&, 50 unsigned long, PassRefPtr<DatabaseCallback>, ExceptionCode& ec) 51 { 52 // FIXME: uncomment the assert once we use the ScriptExecutionContext* parameter 53 //ASSERT(context->isContextThread()); 50 if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) { 51 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data()); 52 return 0; 53 } 54 54 55 ec = SECURITY_ERR; 56 return 0; 55 RefPtr<DatabaseSync> database = adoptRef(new DatabaseSync(context, name, expectedVersion, displayName, estimatedSize)); 56 57 if (!database->performOpenAndVerify(!creationCallback, ec)) { 58 LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); 59 DatabaseTracker::tracker().removeOpenDatabase(database.get()); 60 return 0; 61 } 62 63 DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize); 64 65 if (database->isNew() && creationCallback.get()) { 66 database->m_expectedVersion = ""; 67 LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get()); 68 creationCallback->handleEvent(context, database.get()); 69 } 70 71 return database; 57 72 } 58 73 59 74 DatabaseSync::DatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion, 60 const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback) 61 : m_scriptExecutionContext(context) 62 , m_name(name.crossThreadString()) 63 , m_expectedVersion(expectedVersion.crossThreadString()) 64 , m_displayName(displayName.crossThreadString()) 65 , m_estimatedSize(estimatedSize) 66 , m_creationCallback(creationCallback) 75 const String& displayName, unsigned long estimatedSize) 76 : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize) 67 77 { 68 ASSERT(context->isContextThread());69 78 } 70 79 … … 72 81 { 73 82 ASSERT(m_scriptExecutionContext->isContextThread()); 74 }75 83 76 String DatabaseSync::version() const 77 { 78 ASSERT(m_scriptExecutionContext->isContextThread());79 return String();84 if (opened()) { 85 DatabaseTracker::tracker().removeOpenDatabase(this); 86 closeDatabase(); 87 } 80 88 } 81 89 … … 90 98 } 91 99 92 ScriptExecutionContext* DatabaseSync::scriptExecutionContext() const 100 void DatabaseSync::markAsDeletedAndClose() 93 101 { 94 ASSERT(m_scriptExecutionContext->isContextThread()); 95 return m_scriptExecutionContext.get(); 102 // FIXME: need to do something similar to closeImmediately(), but in a sync way 103 } 104 105 class CloseSyncDatabaseOnContextThreadTask : public ScriptExecutionContext::Task { 106 public: 107 static PassOwnPtr<CloseSyncDatabaseOnContextThreadTask> create(PassRefPtr<DatabaseSync> database) 108 { 109 return new CloseSyncDatabaseOnContextThreadTask(database); 110 } 111 112 virtual void performTask(ScriptExecutionContext*) 113 { 114 m_database->closeImmediately(); 115 } 116 117 private: 118 CloseSyncDatabaseOnContextThreadTask(PassRefPtr<DatabaseSync> database) 119 : m_database(database) 120 { 121 } 122 123 RefPtr<DatabaseSync> m_database; 124 }; 125 126 void DatabaseSync::closeImmediately() 127 { 128 if (!m_scriptExecutionContext->isContextThread()) { 129 m_scriptExecutionContext->postTask(CloseSyncDatabaseOnContextThreadTask::create(this)); 130 return; 131 } 132 133 if (!opened()) 134 return; 135 136 DatabaseTracker::tracker().removeOpenDatabase(this); 137 138 closeDatabase(); 96 139 } 97 140 -
trunk/WebCore/storage/DatabaseSync.h
r61866 r62153 31 31 32 32 #if ENABLE(DATABASE) 33 #include "AbstractDatabase.h" 33 34 #include "PlatformString.h" 34 35 #include <wtf/Forward.h> 36 #ifndef NDEBUG 37 #include "SecurityOrigin.h" 38 #endif 35 39 36 40 namespace WebCore { … … 39 43 class SQLTransactionSyncCallback; 40 44 class ScriptExecutionContext; 41 42 typedef int ExceptionCode; 45 class SecurityOrigin; 43 46 44 47 // Instances of this class should be created and used only on the worker's context thread. 45 class DatabaseSync : public RefCounted<DatabaseSync>{48 class DatabaseSync : public AbstractDatabase { 46 49 public: 47 ~DatabaseSync();50 virtual ~DatabaseSync(); 48 51 49 // Direct support for the DOM API50 52 static PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, 51 53 const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&); 52 String version() const;53 54 void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); 54 55 void transaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&); 55 56 56 // Internal engine support 57 ScriptExecutionContext* scriptExecutionContext() const; 58 59 static const String& databaseInfoTableName(); 57 virtual void markAsDeletedAndClose(); 58 virtual void closeImmediately(); 60 59 61 60 private: 62 61 DatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, 63 const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>); 64 65 RefPtr<ScriptExecutionContext> m_scriptExecutionContext; 66 String m_name; 67 String m_expectedVersion; 68 String m_displayName; 69 unsigned long m_estimatedSize; 70 RefPtr<DatabaseCallback> m_creationCallback; 71 72 #ifndef NDEBUG 73 String databaseDebugName() const { return String(); } 74 #endif 62 const String& displayName, unsigned long estimatedSize); 75 63 }; 76 64 -
trunk/WebCore/storage/DatabaseTask.cpp
r61866 r62153 88 88 // Opens the database file and verifies the version matches the expected version. 89 89 90 DatabaseOpenTask::DatabaseOpenTask(Database* database, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)90 DatabaseOpenTask::DatabaseOpenTask(Database* database, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success) 91 91 : DatabaseTask(database, synchronizer) 92 , m_setVersionInNewDatabase(setVersionInNewDatabase) 92 93 , m_code(code) 93 94 , m_success(success) … … 98 99 void DatabaseOpenTask::doPerformTask() 99 100 { 100 m_success = database()->performOpenAndVerify(m_ code);101 m_success = database()->performOpenAndVerify(m_setVersionInNewDatabase, m_code); 101 102 } 102 103 -
trunk/WebCore/storage/DatabaseTask.h
r61866 r62153 86 86 class DatabaseOpenTask : public DatabaseTask { 87 87 public: 88 static PassOwnPtr<DatabaseOpenTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)88 static PassOwnPtr<DatabaseOpenTask> create(Database* db, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success) 89 89 { 90 return new DatabaseOpenTask(db, s ynchronizer, code, success);90 return new DatabaseOpenTask(db, setVersionInNewDatabase, synchronizer, code, success); 91 91 } 92 92 93 93 private: 94 DatabaseOpenTask(Database*, DatabaseTaskSynchronizer*, ExceptionCode&, bool& success);94 DatabaseOpenTask(Database*, bool setVersionInNewDatabase, DatabaseTaskSynchronizer*, ExceptionCode&, bool& success); 95 95 96 96 virtual void doPerformTask(); … … 99 99 #endif 100 100 101 bool m_setVersionInNewDatabase; 101 102 ExceptionCode& m_code; 102 103 bool& m_success; -
trunk/WebKit/chromium/ChangeLog
r62136 r62153 1 2010-06-29 Dumitru Daniliuc <dumi@chromium.org> 2 3 Reviewed by Darin Fisher. 4 5 Simplify the WebDatabase interface. 6 https://bugs.webkit.org/show_bug.cgi?id=40607 7 8 Do not ref()/deref() the private AbstractDatabase member. This 9 allows us to use WebDatabase in the destructors of the DB 10 classes. 11 12 * public/WebDatabase.h: 13 (WebKit::WebDatabase::WebDatabase): 14 * src/WebDatabase.cpp: 15 (WebKit::WebDatabase::name): 16 (WebKit::WebDatabase::displayName): 17 (WebKit::WebDatabase::estimatedSize): 18 (WebKit::WebDatabase::securityOrigin): 19 (WebKit::WebDatabase::WebDatabase): 20 1 21 2010-06-29 Zhe Su <suzhe@chromium.org> 2 22 -
trunk/WebKit/chromium/public/WebDatabase.h
r61154 r62153 35 35 #include "WebSecurityOrigin.h" 36 36 37 #if WEBKIT_IMPLEMENTATION38 37 namespace WebCore { class AbstractDatabase; } 39 namespace WTF { template <typename T> class PassRefPtr; }40 #endif41 38 42 39 namespace WebKit { 43 40 44 41 class WebDatabaseObserver; 45 class WebDatabasePrivate;46 42 class WebString; 47 43 48 44 class WebDatabase { 49 45 public: 50 WebDatabase() : m_private(0) { }51 WebDatabase(const WebDatabase& d) : m_private(0) { assign(d); }52 ~WebDatabase() { reset(); }53 54 WebDatabase& operator=(const WebDatabase& d)55 {56 assign(d);57 return *this;58 }59 60 WEBKIT_API void reset();61 WEBKIT_API void assign(const WebDatabase&);62 bool isNull() const { return !m_private; }63 64 46 WEBKIT_API WebString name() const; 65 47 WEBKIT_API WebString displayName() const; … … 77 59 78 60 #if WEBKIT_IMPLEMENTATION 79 WebDatabase(const WTF::PassRefPtr<WebCore::AbstractDatabase>&); 80 WebDatabase& operator=(const WTF::PassRefPtr<WebCore::AbstractDatabase>&); 81 operator WTF::PassRefPtr<WebCore::AbstractDatabase>() const; 61 WebDatabase(const WebCore::AbstractDatabase*); 82 62 #endif 83 63 84 64 private: 85 void assign(WebDatabasePrivate*); 86 87 WebDatabasePrivate* m_private; 65 WebDatabase() { } 66 const WebCore::AbstractDatabase* m_database; 88 67 }; 89 68 -
trunk/WebKit/chromium/src/WebDatabase.cpp
r61154 r62153 33 33 34 34 #include "AbstractDatabase.h" 35 #include "DatabaseTask.h"36 #include "DatabaseThread.h"37 35 #include "DatabaseTracker.h" 38 #include "Document.h"39 #include "KURL.h"40 36 #include "QuotaTracker.h" 41 37 #include "SecurityOrigin.h" … … 51 47 static WebDatabaseObserver* databaseObserver = 0; 52 48 53 class WebDatabasePrivate : public AbstractDatabase {54 };55 56 void WebDatabase::reset()57 {58 assign(0);59 }60 61 void WebDatabase::assign(const WebDatabase& other)62 {63 WebDatabasePrivate* d = const_cast<WebDatabasePrivate*>(other.m_private);64 if (d)65 d->ref();66 assign(d);67 }68 69 49 WebString WebDatabase::name() const 70 50 { 71 ASSERT(m_ private);72 return m_ private->stringIdentifier();51 ASSERT(m_database); 52 return m_database->stringIdentifier(); 73 53 } 74 54 75 55 WebString WebDatabase::displayName() const 76 56 { 77 ASSERT(m_ private);78 return m_ private->displayName();57 ASSERT(m_database); 58 return m_database->displayName(); 79 59 } 80 60 81 61 unsigned long WebDatabase::estimatedSize() const 82 62 { 83 ASSERT(m_ private);84 return m_ private->estimatedSize();63 ASSERT(m_database); 64 return m_database->estimatedSize(); 85 65 } 86 66 87 67 WebSecurityOrigin WebDatabase::securityOrigin() const 88 68 { 89 ASSERT(m_ private);90 return WebSecurityOrigin(m_ private->securityOrigin());69 ASSERT(m_database); 70 return WebSecurityOrigin(m_database->securityOrigin()); 91 71 } 92 72 … … 118 98 } 119 99 120 WebDatabase::WebDatabase(const WTF::PassRefPtr<AbstractDatabase>&database)121 : m_ private(static_cast<WebDatabasePrivate*>(database.releaseRef()))100 WebDatabase::WebDatabase(const AbstractDatabase* database) 101 : m_database(database) 122 102 { 123 103 } 124 104 125 WebDatabase& WebDatabase::operator=(const WTF::PassRefPtr<AbstractDatabase>& database)126 {127 assign(static_cast<WebDatabasePrivate*>(database.releaseRef()));128 return *this;129 }130 131 WebDatabase::operator WTF::PassRefPtr<AbstractDatabase>() const132 {133 return PassRefPtr<AbstractDatabase>(const_cast<WebDatabasePrivate*>(m_private));134 }135 136 void WebDatabase::assign(WebDatabasePrivate* d)137 {138 // d is already ref'd for us by the caller139 if (m_private)140 m_private->deref();141 m_private = d;142 }143 144 105 } // namespace WebKit
Note: See TracChangeset
for help on using the changeset viewer.