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 "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