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 "DatabaseManager.h" 28 29#if ENABLE(SQL_DATABASE) 30 31#include "AbstractDatabaseServer.h" 32#include "Database.h" 33#include "DatabaseBackend.h" 34#include "DatabaseBackendBase.h" 35#include "DatabaseBackendContext.h" 36#include "DatabaseBackendSync.h" 37#include "DatabaseCallback.h" 38#include "DatabaseContext.h" 39#include "DatabaseStrategy.h" 40#include "DatabaseSync.h" 41#include "DatabaseTask.h" 42#include "ExceptionCode.h" 43#include "InspectorDatabaseInstrumentation.h" 44#include "Logging.h" 45#include "PlatformStrategies.h" 46#include "ScriptController.h" 47#include "ScriptExecutionContext.h" 48#include "SecurityOrigin.h" 49 50namespace WebCore { 51 52DatabaseManager::ProposedDatabase::ProposedDatabase(DatabaseManager& manager, 53 SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize) 54 : m_manager(manager) 55 , m_origin(origin->isolatedCopy()) 56 , m_details(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0, 0, 0) 57{ 58 m_manager.addProposedDatabase(this); 59} 60 61DatabaseManager::ProposedDatabase::~ProposedDatabase() 62{ 63 m_manager.removeProposedDatabase(this); 64} 65 66DatabaseManager& DatabaseManager::manager() 67{ 68 static DatabaseManager* dbManager = 0; 69 // FIXME: The following is vulnerable to a race between threads. Need to 70 // implement a thread safe on-first-use static initializer. 71 if (!dbManager) 72 dbManager = new DatabaseManager(); 73 74 return *dbManager; 75} 76 77DatabaseManager::DatabaseManager() 78 : m_server(platformStrategies()->databaseStrategy()->getDatabaseServer()) 79 , m_client(0) 80 , m_databaseIsAvailable(true) 81#if !ASSERT_DISABLED 82 , m_databaseContextRegisteredCount(0) 83 , m_databaseContextInstanceCount(0) 84#endif 85{ 86 ASSERT(m_server); // We should always have a server to work with. 87} 88 89void DatabaseManager::initialize(const String& databasePath) 90{ 91 m_server->initialize(databasePath); 92} 93 94void DatabaseManager::setClient(DatabaseManagerClient* client) 95{ 96 m_client = client; 97 m_server->setClient(client); 98} 99 100String DatabaseManager::databaseDirectoryPath() const 101{ 102 return m_server->databaseDirectoryPath(); 103} 104 105void DatabaseManager::setDatabaseDirectoryPath(const String& path) 106{ 107 m_server->setDatabaseDirectoryPath(path); 108} 109 110bool DatabaseManager::isAvailable() 111{ 112 return m_databaseIsAvailable; 113} 114 115void DatabaseManager::setIsAvailable(bool available) 116{ 117 m_databaseIsAvailable = available; 118} 119 120PassRefPtr<DatabaseContext> DatabaseManager::existingDatabaseContextFor(ScriptExecutionContext* context) 121{ 122 std::lock_guard<std::mutex> lock(m_mutex); 123 124 ASSERT(m_databaseContextRegisteredCount >= 0); 125 ASSERT(m_databaseContextInstanceCount >= 0); 126 ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount); 127 128 RefPtr<DatabaseContext> databaseContext = adoptRef(m_contextMap.get(context)); 129 if (databaseContext) { 130 // If we're instantiating a new DatabaseContext, the new instance would 131 // carry a new refCount of 1. The client expects this and will simply 132 // adoptRef the databaseContext without ref'ing it. 133 // However, instead of instantiating a new instance, we're reusing 134 // an existing one that corresponds to the specified ScriptExecutionContext. 135 // Hence, that new refCount need to be attributed to the reused instance 136 // to ensure that the refCount is accurate when the client adopts the ref. 137 // We do this by ref'ing the reused databaseContext before returning it. 138 databaseContext->ref(); 139 } 140 return databaseContext.release(); 141} 142 143PassRefPtr<DatabaseContext> DatabaseManager::databaseContextFor(ScriptExecutionContext* context) 144{ 145 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 146 if (!databaseContext) 147 databaseContext = adoptRef(new DatabaseContext(context)); 148 return databaseContext.release(); 149} 150 151void DatabaseManager::registerDatabaseContext(DatabaseContext* databaseContext) 152{ 153 std::lock_guard<std::mutex> lock(m_mutex); 154 155 ScriptExecutionContext* context = databaseContext->scriptExecutionContext(); 156 m_contextMap.set(context, databaseContext); 157#if !ASSERT_DISABLED 158 m_databaseContextRegisteredCount++; 159#endif 160} 161 162void DatabaseManager::unregisterDatabaseContext(DatabaseContext* databaseContext) 163{ 164 std::lock_guard<std::mutex> lock(m_mutex); 165 166 ScriptExecutionContext* context = databaseContext->scriptExecutionContext(); 167 ASSERT(m_contextMap.get(context)); 168#if !ASSERT_DISABLED 169 m_databaseContextRegisteredCount--; 170#endif 171 m_contextMap.remove(context); 172} 173 174#if !ASSERT_DISABLED 175void DatabaseManager::didConstructDatabaseContext() 176{ 177 std::lock_guard<std::mutex> lock(m_mutex); 178 179 m_databaseContextInstanceCount++; 180} 181 182void DatabaseManager::didDestructDatabaseContext() 183{ 184 std::lock_guard<std::mutex> lock(m_mutex); 185 186 m_databaseContextInstanceCount--; 187 ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount); 188} 189#endif 190 191ExceptionCode DatabaseManager::exceptionCodeForDatabaseError(DatabaseError error) 192{ 193 switch (error) { 194 case DatabaseError::None: 195 return 0; 196 case DatabaseError::DatabaseIsBeingDeleted: 197 case DatabaseError::DatabaseSizeExceededQuota: 198 case DatabaseError::DatabaseSizeOverflowed: 199 case DatabaseError::GenericSecurityError: 200 return SECURITY_ERR; 201 case DatabaseError::InvalidDatabaseState: 202 return INVALID_STATE_ERR; 203 } 204 ASSERT_NOT_REACHED(); 205 return 0; // Make some older compilers happy. 206} 207 208static void logOpenDatabaseError(ScriptExecutionContext* context, const String& name) 209{ 210 UNUSED_PARAM(context); 211 UNUSED_PARAM(name); 212 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), 213 context->securityOrigin()->toString().ascii().data()); 214} 215 216PassRefPtr<DatabaseBackendBase> DatabaseManager::openDatabaseBackend(ScriptExecutionContext* context, 217 DatabaseType type, const String& name, const String& expectedVersion, const String& displayName, 218 unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) 219{ 220 ASSERT(error == DatabaseError::None); 221 222 RefPtr<DatabaseContext> databaseContext = databaseContextFor(context); 223 RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend(); 224 225 RefPtr<DatabaseBackendBase> backend = m_server->openDatabase(backendContext, type, name, expectedVersion, 226 displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); 227 228 if (!backend) { 229 ASSERT(error != DatabaseError::None); 230 231 switch (error) { 232 case DatabaseError::DatabaseIsBeingDeleted: 233 case DatabaseError::DatabaseSizeOverflowed: 234 case DatabaseError::GenericSecurityError: 235 logOpenDatabaseError(context, name); 236 return 0; 237 238 case DatabaseError::InvalidDatabaseState: 239 logErrorMessage(context, errorMessage); 240 return 0; 241 242 case DatabaseError::DatabaseSizeExceededQuota: 243 // Notify the client that we've exceeded the database quota. 244 // The client may want to increase the quota, and we'll give it 245 // one more try after if that is the case. 246 { 247 ProposedDatabase proposedDb(*this, context->securityOrigin(), name, displayName, estimatedSize); 248 databaseContext->databaseExceededQuota(name, proposedDb.details()); 249 } 250 error = DatabaseError::None; 251 252 backend = m_server->openDatabase(backendContext, type, name, expectedVersion, 253 displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage, 254 AbstractDatabaseServer::RetryOpenDatabase); 255 break; 256 257 default: 258 ASSERT_NOT_REACHED(); 259 } 260 261 if (!backend) { 262 ASSERT(error != DatabaseError::None); 263 264 if (error == DatabaseError::InvalidDatabaseState) { 265 logErrorMessage(context, errorMessage); 266 return 0; 267 } 268 269 logOpenDatabaseError(context, name); 270 return 0; 271 } 272 } 273 274 return backend.release(); 275} 276 277void DatabaseManager::addProposedDatabase(ProposedDatabase* proposedDb) 278{ 279 std::lock_guard<std::mutex> lock(m_mutex); 280 281 m_proposedDatabases.add(proposedDb); 282} 283 284void DatabaseManager::removeProposedDatabase(ProposedDatabase* proposedDb) 285{ 286 std::lock_guard<std::mutex> lock(m_mutex); 287 288 m_proposedDatabases.remove(proposedDb); 289} 290 291PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context, 292 const String& name, const String& expectedVersion, const String& displayName, 293 unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, 294 DatabaseError& error) 295{ 296 ScriptController::initializeThreading(); 297 ASSERT(error == DatabaseError::None); 298 299 bool setVersionInNewDatabase = !creationCallback; 300 String errorMessage; 301 RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Async, name, 302 expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); 303 if (!backend) 304 return 0; 305 306 RefPtr<Database> database = Database::create(context, backend); 307 308 RefPtr<DatabaseContext> databaseContext = databaseContextFor(context); 309 databaseContext->setHasOpenDatabases(); 310 InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion); 311 312 if (backend->isNew() && creationCallback.get()) { 313 LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get()); 314 database->m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) { 315 creationCallback->handleEvent(database.get()); 316 }); 317 } 318 319 ASSERT(database); 320 return database.release(); 321} 322 323PassRefPtr<DatabaseSync> DatabaseManager::openDatabaseSync(ScriptExecutionContext* context, 324 const String& name, const String& expectedVersion, const String& displayName, 325 unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, DatabaseError& error) 326{ 327 ASSERT(context->isContextThread()); 328 ASSERT(error == DatabaseError::None); 329 330 bool setVersionInNewDatabase = !creationCallback; 331 String errorMessage; 332 RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Sync, name, 333 expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage); 334 if (!backend) 335 return 0; 336 337 RefPtr<DatabaseSync> database = DatabaseSync::create(context, backend); 338 339 if (backend->isNew() && creationCallback.get()) { 340 LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get()); 341 creationCallback->handleEvent(database.get()); 342 } 343 344 ASSERT(database); 345 return database.release(); 346} 347 348bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext* context) 349{ 350 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 351 if (!databaseContext) 352 return false; 353 return databaseContext->hasOpenDatabases(); 354} 355 356void DatabaseManager::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* synchronizer) 357{ 358 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 359 if (!databaseContext || !databaseContext->stopDatabases(synchronizer)) 360 if (synchronizer) 361 synchronizer->taskCompleted(); 362} 363 364String DatabaseManager::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist) 365{ 366 { 367 std::lock_guard<std::mutex> lock(m_mutex); 368 369 for (auto* proposedDatabase : m_proposedDatabases) { 370 if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin)) 371 return String(); 372 } 373 } 374 375 return m_server->fullPathForDatabase(origin, name, createIfDoesNotExist); 376} 377 378bool DatabaseManager::hasEntryForOrigin(SecurityOrigin* origin) 379{ 380 return m_server->hasEntryForOrigin(origin); 381} 382 383void DatabaseManager::origins(Vector<RefPtr<SecurityOrigin>>& result) 384{ 385 m_server->origins(result); 386} 387 388bool DatabaseManager::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& result) 389{ 390 return m_server->databaseNamesForOrigin(origin, result); 391} 392 393DatabaseDetails DatabaseManager::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) 394{ 395 { 396 std::lock_guard<std::mutex> lock(m_mutex); 397 398 for (auto* proposedDatabase : m_proposedDatabases) { 399 if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin)) { 400 ASSERT(proposedDatabase->details().threadID() == std::this_thread::get_id() || isMainThread()); 401 402 return proposedDatabase->details(); 403 } 404 } 405 } 406 407 return m_server->detailsForNameAndOrigin(name, origin); 408} 409 410unsigned long long DatabaseManager::usageForOrigin(SecurityOrigin* origin) 411{ 412 return m_server->usageForOrigin(origin); 413} 414 415unsigned long long DatabaseManager::quotaForOrigin(SecurityOrigin* origin) 416{ 417 return m_server->quotaForOrigin(origin); 418} 419 420void DatabaseManager::setQuota(SecurityOrigin* origin, unsigned long long quotaSize) 421{ 422 m_server->setQuota(origin, quotaSize); 423} 424 425void DatabaseManager::deleteAllDatabases() 426{ 427 m_server->deleteAllDatabases(); 428} 429 430bool DatabaseManager::deleteOrigin(SecurityOrigin* origin) 431{ 432 return m_server->deleteOrigin(origin); 433} 434 435bool DatabaseManager::deleteDatabase(SecurityOrigin* origin, const String& name) 436{ 437 return m_server->deleteDatabase(origin, name); 438} 439 440void DatabaseManager::interruptAllDatabasesForContext(ScriptExecutionContext* context) 441{ 442 RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context); 443 if (databaseContext) 444 m_server->interruptAllDatabasesForContext(databaseContext->backend().get()); 445} 446 447void DatabaseManager::logErrorMessage(ScriptExecutionContext* context, const String& message) 448{ 449 context->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message); 450} 451 452} // namespace WebCore 453 454#endif // ENABLE(SQL_DATABASE) 455