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