Home | History | Annotate | Download | only in webdatabase
      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