1 /* 2 * Copyright (C) 2012 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/DatabaseManager.h" 28 29 #include "core/dom/ExceptionCode.h" 30 #include "core/dom/ScriptExecutionContext.h" 31 #include "core/inspector/InspectorDatabaseInstrumentation.h" 32 #include "core/platform/Logging.h" 33 #include "modules/webdatabase/AbstractDatabaseServer.h" 34 #include "modules/webdatabase/Database.h" 35 #include "modules/webdatabase/DatabaseBackend.h" 36 #include "modules/webdatabase/DatabaseBackendBase.h" 37 #include "modules/webdatabase/DatabaseBackendContext.h" 38 #include "modules/webdatabase/DatabaseBackendSync.h" 39 #include "modules/webdatabase/DatabaseCallback.h" 40 #include "modules/webdatabase/DatabaseContext.h" 41 #include "modules/webdatabase/DatabaseServer.h" 42 #include "modules/webdatabase/DatabaseSync.h" 43 #include "modules/webdatabase/DatabaseTask.h" 44 #include "weborigin/SecurityOrigin.h" 45 46 namespace WebCore { 47 48 DatabaseManager& DatabaseManager::manager() 49 { 50 static DatabaseManager* dbManager = 0; 51 // FIXME: The following is vulnerable to a race between threads. Need to 52 // implement a thread safe on-first-use static initializer. 53 if (!dbManager) 54 dbManager = new DatabaseManager(); 55 56 return *dbManager; 57 } 58 59 DatabaseManager::DatabaseManager() 60 #if !ASSERT_DISABLED 61 : m_databaseContextRegisteredCount(0) 62 , m_databaseContextInstanceCount(0) 63 #endif 64 { 65 m_server = new DatabaseServer; 66 ASSERT(m_server); // We should always have a server to work with. 67 } 68 69 class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task { 70 public: 71 static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback) 72 { 73 return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback)); 74 } 75 76 virtual void performTask(ScriptExecutionContext*) 77 { 78 m_creationCallback->handleEvent(m_database.get()); 79 } 80 81 private: 82 DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback) 83 : m_database(database) 84 , m_creationCallback(callback) 85 { 86 } 87 88 RefPtr<Database> m_database; 89 RefPtr<DatabaseCallback> m_creationCallback; 90 }; 91 92 PassRefPtr<DatabaseContext> DatabaseManager::existingDatabaseContextFor(ScriptExecutionContext* context) 93 { 94 MutexLocker locker(m_contextMapLock); 95 96 ASSERT(m_databaseContextRegisteredCount >= 0); 97 ASSERT(m_databaseContextInstanceCount >= 0); 98 ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount); 99 100 RefPtr<DatabaseContext> databaseContext = adoptRef(m_contextMap.get(context)); 101 if (databaseContext) { 102 // If we're instantiating a new DatabaseContext, the new instance would 103 // carry a new refCount of 1. The client expects this and will simply 104 // adoptRef the databaseContext without ref'ing it. 105 // However, instead of instantiating a new instance, we're reusing 106 // an existing one that corresponds to the specified ScriptExecutionContext. 107 // Hence, that new refCount need to be attributed to the reused instance 108 // to ensure that the refCount is accurate when the client adopts the ref. 109 // We do this by ref'ing the reused databaseContext before returning it. 110 databaseContext->ref(); 111 } 112 return databaseContext.release(); 113 } 114 115 PassRefPtr<DatabaseContext> DatabaseManager::databaseContextFor(ScriptExecutionContext* context) 116 { 117 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 118 if (!databaseContext) 119 databaseContext = adoptRef(new DatabaseContext(context)); 120 return databaseContext.release(); 121 } 122 123 void DatabaseManager::registerDatabaseContext(DatabaseContext* databaseContext) 124 { 125 MutexLocker locker(m_contextMapLock); 126 ScriptExecutionContext* context = databaseContext->scriptExecutionContext(); 127 m_contextMap.set(context, databaseContext); 128 #if !ASSERT_DISABLED 129 m_databaseContextRegisteredCount++; 130 #endif 131 } 132 133 void DatabaseManager::unregisterDatabaseContext(DatabaseContext* databaseContext) 134 { 135 MutexLocker locker(m_contextMapLock); 136 ScriptExecutionContext* context = databaseContext->scriptExecutionContext(); 137 ASSERT(m_contextMap.get(context)); 138 #if !ASSERT_DISABLED 139 m_databaseContextRegisteredCount--; 140 #endif 141 m_contextMap.remove(context); 142 } 143 144 #if !ASSERT_DISABLED 145 void DatabaseManager::didConstructDatabaseContext() 146 { 147 MutexLocker lock(m_contextMapLock); 148 m_databaseContextInstanceCount++; 149 } 150 151 void DatabaseManager::didDestructDatabaseContext() 152 { 153 MutexLocker lock(m_contextMapLock); 154 m_databaseContextInstanceCount--; 155 ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount); 156 } 157 #endif 158 159 ExceptionCode DatabaseManager::exceptionCodeForDatabaseError(DatabaseError error) 160 { 161 switch (error) { 162 case DatabaseError::None: 163 return 0; 164 case DatabaseError::DatabaseIsBeingDeleted: 165 case DatabaseError::DatabaseSizeExceededQuota: 166 case DatabaseError::DatabaseSizeOverflowed: 167 case DatabaseError::GenericSecurityError: 168 return SecurityError; 169 case DatabaseError::InvalidDatabaseState: 170 return InvalidStateError; 171 } 172 ASSERT_NOT_REACHED(); 173 return 0; // Make some older compilers happy. 174 } 175 176 static void logOpenDatabaseError(ScriptExecutionContext* context, const String& name) 177 { 178 UNUSED_PARAM(context); 179 UNUSED_PARAM(name); 180 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), 181 context->securityOrigin()->toString().ascii().data()); 182 } 183 184 PassRefPtr<DatabaseBackendBase> DatabaseManager::openDatabaseBackend(ScriptExecutionContext* context, 185 DatabaseType type, const String& name, const String& expectedVersion, const String& displayName, 186 unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) 187 { 188 ASSERT(error == DatabaseError::None); 189 190 RefPtr<DatabaseContext> databaseContext = databaseContextFor(context); 191 RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend(); 192 193 RefPtr<DatabaseBackendBase> backend = m_server->openDatabase(backendContext, type, name, expectedVersion, 194 displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); 195 196 if (!backend) { 197 ASSERT(error != DatabaseError::None); 198 199 switch (error) { 200 case DatabaseError::DatabaseIsBeingDeleted: 201 case DatabaseError::DatabaseSizeOverflowed: 202 case DatabaseError::GenericSecurityError: 203 case DatabaseError::DatabaseSizeExceededQuota: 204 logOpenDatabaseError(context, name); 205 return 0; 206 207 case DatabaseError::InvalidDatabaseState: 208 logErrorMessage(context, errorMessage); 209 return 0; 210 211 default: 212 ASSERT_NOT_REACHED(); 213 } 214 } 215 216 return backend.release(); 217 } 218 219 PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context, 220 const String& name, const String& expectedVersion, const String& displayName, 221 unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, 222 DatabaseError& error) 223 { 224 ASSERT(error == DatabaseError::None); 225 226 bool setVersionInNewDatabase = !creationCallback; 227 String errorMessage; 228 RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Async, name, 229 expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); 230 if (!backend) 231 return 0; 232 233 RefPtr<Database> database = Database::create(context, backend); 234 235 RefPtr<DatabaseContext> databaseContext = databaseContextFor(context); 236 databaseContext->setHasOpenDatabases(); 237 InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion); 238 239 if (backend->isNew() && creationCallback.get()) { 240 LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get()); 241 database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback)); 242 } 243 244 ASSERT(database); 245 return database.release(); 246 } 247 248 PassRefPtr<DatabaseSync> DatabaseManager::openDatabaseSync(ScriptExecutionContext* context, 249 const String& name, const String& expectedVersion, const String& displayName, 250 unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, DatabaseError& error) 251 { 252 ASSERT(context->isContextThread()); 253 ASSERT(error == DatabaseError::None); 254 255 bool setVersionInNewDatabase = !creationCallback; 256 String errorMessage; 257 RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Sync, name, 258 expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); 259 if (!backend) 260 return 0; 261 262 RefPtr<DatabaseSync> database = DatabaseSync::create(context, backend); 263 264 if (backend->isNew() && creationCallback.get()) { 265 LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get()); 266 creationCallback->handleEvent(database.get()); 267 } 268 269 ASSERT(database); 270 return database.release(); 271 } 272 273 bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext* context) 274 { 275 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 276 if (!databaseContext) 277 return false; 278 return databaseContext->hasOpenDatabases(); 279 } 280 281 void DatabaseManager::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* synchronizer) 282 { 283 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 284 if (!databaseContext || !databaseContext->stopDatabases(synchronizer)) 285 if (synchronizer) 286 synchronizer->taskCompleted(); 287 } 288 289 String DatabaseManager::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist) 290 { 291 return m_server->fullPathForDatabase(origin, name, createIfDoesNotExist); 292 } 293 294 void DatabaseManager::closeDatabasesImmediately(const String& originIdentifier, const String& name) 295 { 296 m_server->closeDatabasesImmediately(originIdentifier, name); 297 } 298 299 void DatabaseManager::interruptAllDatabasesForContext(ScriptExecutionContext* context) 300 { 301 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 302 if (databaseContext) 303 m_server->interruptAllDatabasesForContext(databaseContext->backend().get()); 304 } 305 306 void DatabaseManager::logErrorMessage(ScriptExecutionContext* context, const String& message) 307 { 308 context->addConsoleMessage(StorageMessageSource, ErrorMessageLevel, message); 309 } 310 311 } // namespace WebCore 312