Changeset 142030 in webkit


Ignore:
Timestamp:
Feb 6, 2013 1:51:58 PM (11 years ago)
Author:
mark.lam@apple.com
Message:

Split openDatabase() between front and back end work.
https://bugs.webkit.org/show_bug.cgi?id=107475.

Reviewed by Anders Carlsson.

The main work of splitting DatabaseManager::openDatabase() is in
refactoring how DatabaseTracker::canEstablishDatabase() works. It used
to check for adequate space quota, and if the check fails, it would call
back into the client from inside canEstablishDatabase(). The call back
allows the client to update the quota (if appropriate). Thereafter,
canEstablishDatabase() will retry its quota check.

In a webkit2 world, we'll want to minimize the traffic between the
client (script side) and the server (sqlite db side), and ideally, we
don't want the server to call back to the client. Note: the
DatabaseTracker belongs on the server side.

To achieve this, we split canEstablishDatabase() into 2 parts: the
checks before the call back to the client, and the checks after.
The first part will retain the name canEstablishDatabase(), and the
second part will be named retryCanEstablishDatabase().
We also added a DatabaseServer::openDatabase() function that can be
called with a retry flag.

The client side DatabaseManager::openDatabase() will call
DatabaseServer::openDatabase(), which then calls canEstablishDatabase()
to do its quota check. If there is enough quota,
DatabaseServer::openDatabase() will proceed to open the backend database
without return to the client first. The opened database will be returned
to the client.

If DatabaseServer::openDatabase() finds inadequate quota the first time,
it will return with a DatabaseSizeExceededQuota error. The DatabaseManager
(on the client side) will check for this error and call back to its client
for an opportunity to increase the quota. Thereafter, the DatabaseManager
will call DatabaseServer::openDatabase() again. This time,
DatabaseServer::openDatabase() will call retryCanEstablishDatabase() to
check the quota, and then open the backend database if there is enough
quota.

No new tests.

  • Modules/webdatabase/AbstractDatabaseServer.h:
  • Modules/webdatabase/DOMWindowWebDatabase.cpp:

(WebCore::DOMWindowWebDatabase::openDatabase):

  • Modules/webdatabase/Database.cpp:

(WebCore::Database::create):

  • Modules/webdatabase/Database.h:

(Database):

  • Modules/webdatabase/DatabaseBackend.cpp:

(WebCore::DatabaseBackend::performOpenAndVerify):

  • Modules/webdatabase/DatabaseBackend.h:

(DatabaseBackend):

  • Modules/webdatabase/DatabaseBackendAsync.cpp:

(WebCore::DatabaseBackendAsync::openAndVerifyVersion):
(WebCore::DatabaseBackendAsync::performOpenAndVerify):

  • Modules/webdatabase/DatabaseBackendAsync.h:

(DatabaseBackendAsync):

  • Modules/webdatabase/DatabaseBackendSync.cpp:

(WebCore::DatabaseBackendSync::openAndVerifyVersion):

  • Modules/webdatabase/DatabaseBackendSync.h:

(DatabaseBackendSync):

  • Modules/webdatabase/DatabaseError.h:

(WebCore::ENUM_CLASS):

  • Modules/webdatabase/DatabaseManager.cpp:

(WebCore::DatabaseManager::exceptionCodeForDatabaseError):
(WebCore::DatabaseManager::openDatabaseBackend):
(WebCore::DatabaseManager::openDatabase):
(WebCore::DatabaseManager::openDatabaseSync):

  • Modules/webdatabase/DatabaseManager.h:

(DatabaseManager):

  • Modules/webdatabase/DatabaseServer.cpp:

(WebCore::DatabaseServer::openDatabase):
(WebCore::DatabaseServer::createDatabase):

  • Modules/webdatabase/DatabaseServer.h:
  • Modules/webdatabase/DatabaseSync.cpp:

(WebCore::DatabaseSync::create):

  • Modules/webdatabase/DatabaseSync.h:

(DatabaseSync):

  • Modules/webdatabase/DatabaseTracker.cpp:

(WebCore::DatabaseTracker::hasAdequateQuotaForOrigin):
(WebCore::DatabaseTracker::canEstablishDatabase):
(WebCore::DatabaseTracker::retryCanEstablishDatabase):

  • Modules/webdatabase/DatabaseTracker.h:

(DatabaseTracker):

  • Modules/webdatabase/WorkerContextWebDatabase.cpp:

(WebCore::WorkerContextWebDatabase::openDatabase):
(WebCore::WorkerContextWebDatabase::openDatabaseSync):

  • Modules/webdatabase/chromium/DatabaseTrackerChromium.cpp:

(WebCore::DatabaseTracker::canEstablishDatabase):
(WebCore::DatabaseTracker::retryCanEstablishDatabase):

Location:
trunk/Source/WebCore
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r142029 r142030  
     12013-02-06  Mark Lam  <mark.lam@apple.com>
     2
     3        Split openDatabase() between front and back end work.
     4        https://bugs.webkit.org/show_bug.cgi?id=107475.
     5
     6        Reviewed by Anders Carlsson.
     7
     8        The main work of splitting DatabaseManager::openDatabase() is in
     9        refactoring how DatabaseTracker::canEstablishDatabase() works. It used
     10        to check for adequate space quota, and if the check fails, it would call
     11        back into the client from inside canEstablishDatabase(). The call back
     12        allows the client to update the quota (if appropriate). Thereafter,
     13        canEstablishDatabase() will retry its quota check.
     14
     15        In a webkit2 world, we'll want to minimize the traffic between the
     16        client (script side) and the server (sqlite db side), and ideally, we
     17        don't want the server to call back to the client. Note: the
     18        DatabaseTracker belongs on the server side.
     19
     20        To achieve this, we split canEstablishDatabase() into 2 parts: the
     21        checks before the call back to the client, and the checks after.
     22        The first part will retain the name canEstablishDatabase(), and the
     23        second part will be named retryCanEstablishDatabase().
     24        We also added a DatabaseServer::openDatabase() function that can be
     25        called with a retry flag.
     26
     27        The client side DatabaseManager::openDatabase() will call
     28        DatabaseServer::openDatabase(), which then calls canEstablishDatabase()
     29        to do its quota check. If there is enough quota,
     30        DatabaseServer::openDatabase() will proceed to open the backend database
     31        without return to the client first. The opened database will be returned
     32        to the client.
     33
     34        If DatabaseServer::openDatabase() finds inadequate quota the first time,
     35        it will return with a DatabaseSizeExceededQuota error. The DatabaseManager
     36        (on the client side) will check for this error and call back to its client
     37        for an opportunity to increase the quota. Thereafter, the DatabaseManager
     38        will call DatabaseServer::openDatabase() again. This time,
     39        DatabaseServer::openDatabase() will call retryCanEstablishDatabase() to
     40        check the quota, and then open the backend database if there is enough
     41        quota.
     42
     43        No new tests.
     44
     45        * Modules/webdatabase/AbstractDatabaseServer.h:
     46        * Modules/webdatabase/DOMWindowWebDatabase.cpp:
     47        (WebCore::DOMWindowWebDatabase::openDatabase):
     48        * Modules/webdatabase/Database.cpp:
     49        (WebCore::Database::create):
     50        * Modules/webdatabase/Database.h:
     51        (Database):
     52        * Modules/webdatabase/DatabaseBackend.cpp:
     53        (WebCore::DatabaseBackend::performOpenAndVerify):
     54        * Modules/webdatabase/DatabaseBackend.h:
     55        (DatabaseBackend):
     56        * Modules/webdatabase/DatabaseBackendAsync.cpp:
     57        (WebCore::DatabaseBackendAsync::openAndVerifyVersion):
     58        (WebCore::DatabaseBackendAsync::performOpenAndVerify):
     59        * Modules/webdatabase/DatabaseBackendAsync.h:
     60        (DatabaseBackendAsync):
     61        * Modules/webdatabase/DatabaseBackendSync.cpp:
     62        (WebCore::DatabaseBackendSync::openAndVerifyVersion):
     63        * Modules/webdatabase/DatabaseBackendSync.h:
     64        (DatabaseBackendSync):
     65        * Modules/webdatabase/DatabaseError.h:
     66        (WebCore::ENUM_CLASS):
     67        * Modules/webdatabase/DatabaseManager.cpp:
     68        (WebCore::DatabaseManager::exceptionCodeForDatabaseError):
     69        (WebCore::DatabaseManager::openDatabaseBackend):
     70        (WebCore::DatabaseManager::openDatabase):
     71        (WebCore::DatabaseManager::openDatabaseSync):
     72        * Modules/webdatabase/DatabaseManager.h:
     73        (DatabaseManager):
     74        * Modules/webdatabase/DatabaseServer.cpp:
     75        (WebCore::DatabaseServer::openDatabase):
     76        (WebCore::DatabaseServer::createDatabase):
     77        * Modules/webdatabase/DatabaseServer.h:
     78        * Modules/webdatabase/DatabaseSync.cpp:
     79        (WebCore::DatabaseSync::create):
     80        * Modules/webdatabase/DatabaseSync.h:
     81        (DatabaseSync):
     82        * Modules/webdatabase/DatabaseTracker.cpp:
     83        (WebCore::DatabaseTracker::hasAdequateQuotaForOrigin):
     84        (WebCore::DatabaseTracker::canEstablishDatabase):
     85        (WebCore::DatabaseTracker::retryCanEstablishDatabase):
     86        * Modules/webdatabase/DatabaseTracker.h:
     87        (DatabaseTracker):
     88        * Modules/webdatabase/WorkerContextWebDatabase.cpp:
     89        (WebCore::WorkerContextWebDatabase::openDatabase):
     90        (WebCore::WorkerContextWebDatabase::openDatabaseSync):
     91        * Modules/webdatabase/chromium/DatabaseTrackerChromium.cpp:
     92        (WebCore::DatabaseTracker::canEstablishDatabase):
     93        (WebCore::DatabaseTracker::retryCanEstablishDatabase):
     94
    1952013-02-06  Tony Gentilcore  <tonyg@chromium.org>
    296
  • trunk/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h

    r141928 r142030  
    2929#if ENABLE(SQL_DATABASE)
    3030
     31#include "DatabaseBasicTypes.h"
    3132#include "DatabaseDetails.h"
     33#include "DatabaseError.h"
    3234#include <wtf/RefPtr.h>
    3335#include <wtf/Vector.h>
     
    3739
    3840class DatabaseBackend;
    39 class DatabaseBackendAsync;
    4041class DatabaseBackendContext;
    41 class DatabaseBackendSync;
    4242class DatabaseManagerClient;
    4343class SecurityOrigin;
     
    5252
    5353    virtual String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true) = 0;
     54
     55    enum OpenAttempt {
     56        FirstTryToOpenDatabase,
     57        RetryOpenDatabase
     58    };
     59
     60    virtual PassRefPtr<DatabaseBackend> openDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError&, String& errorMessage, OpenAttempt = FirstTryToOpenDatabase) = 0;
    5461
    5562#if !PLATFORM(CHROMIUM)
     
    7885    virtual void interruptAllDatabasesForContext(const DatabaseBackendContext*) = 0;
    7986
    80     virtual bool canEstablishDatabase(DatabaseBackendContext*, const String& name, const String& displayName, unsigned long estimatedSize) = 0;
    81 
    82     virtual void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize) = 0;
    8387    virtual unsigned long long getMaxSizeForDatabase(const DatabaseBackend*) = 0;
    8488
  • trunk/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp

    r141668 r142030  
    4949    DatabaseManager& dbManager = DatabaseManager::manager();
    5050    DatabaseError error = DatabaseError::None;
    51     if (dbManager.isAvailable() && window->document()->securityOrigin()->canAccessDatabase(window->document()->topOrigin()))
     51    if (dbManager.isAvailable() && window->document()->securityOrigin()->canAccessDatabase(window->document()->topOrigin())) {
    5252        database = dbManager.openDatabase(window->document(), name, version, displayName, estimatedSize, creationCallback, error);
    53 
    54     ASSERT(error == DatabaseError::None || error == DatabaseError::CannotOpenDatabase);
    55     if (error == DatabaseError::CannotOpenDatabase)
    56         ec = INVALID_STATE_ERR;
    57     if (!database && !ec)
     53        ASSERT(database || error != DatabaseError::None);
     54        ec = DatabaseManager::exceptionCodeForDatabaseError(error);
     55    } else
    5856        ec = SECURITY_ERR;
    5957
  • trunk/Source/WebCore/Modules/webdatabase/Database.cpp

    r141956 r142030  
    6767namespace WebCore {
    6868
     69PassRefPtr<Database> Database::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackend> backend)
     70{
     71    return static_cast<Database*>(backend.get());
     72}
     73
    6974Database::Database(PassRefPtr<DatabaseBackendContext> databaseContext,
    7075    const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
     
    134139}
    135140
    136 bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
    137 {
    138     DatabaseTaskSynchronizer synchronizer;
    139     if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
    140         return false;
    141 
    142 #if PLATFORM(CHROMIUM)
    143     DatabaseTracker::tracker().prepareToOpenDatabase(this);
    144 #endif
    145     bool success = false;
    146     OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
    147     databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
    148     synchronizer.waitForTaskCompletion();
    149 
    150     return success;
    151 }
    152 
    153141void Database::markAsDeletedAndClose()
    154142{
     
    203191{
    204192    return DatabaseManager::manager().getMaxSizeForDatabase(this);
    205 }
    206 
    207 bool Database::performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
    208 {
    209     if (DatabaseBackend::performOpenAndVerify(setVersionInNewDatabase, error, errorMessage)) {
    210         if (databaseContext()->databaseThread())
    211             databaseContext()->databaseThread()->recordDatabaseOpen(this);
    212 
    213         return true;
    214     }
    215 
    216     return false;
    217193}
    218194
  • trunk/Source/WebCore/Modules/webdatabase/Database.h

    r141956 r142030  
    8989        const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
    9090    PassRefPtr<DatabaseBackendAsync> backend();
     91    static PassRefPtr<Database> create(ScriptExecutionContext*, PassRefPtr<DatabaseBackend>);
    9192
    9293    void runTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>,
    9394                        PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper>, bool readOnly);
    94 
    95     bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
    96     virtual bool performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
    9795
    9896    void inProgressTransactionCompleted();
     
    111109
    112110    friend class DatabaseManager;
     111    friend class DatabaseServer; // FIXME: remove this when the backend has been split out.
    113112    friend class DatabaseBackendAsync; // FIXME: remove this when the backend has been split out.
    114113};
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp

    r141950 r142030  
    315315    ASSERT(errorMessage.isEmpty());
    316316    ASSERT(error == DatabaseError::None); // Better not have any errors already.
    317     error = DatabaseError::CannotOpenDatabase; // Presumed failure. We'll clear it if we succeed below.
     317    error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below.
    318318
    319319    const int maxSqliteBusyWaitTime = 30000;
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseBackend.h

    r141956 r142030  
    104104    void closeDatabase();
    105105
     106    virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage) = 0;
    106107    virtual bool performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError&, String& errorMessage);
    107108
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp

    r141950 r142030  
    3030
    3131#include "DatabaseBackendContext.h"
     32#include "DatabaseTask.h"
     33#include "DatabaseThread.h"
     34#include "DatabaseTracker.h"
    3235
    3336namespace WebCore {
     
    3841}
    3942
     43bool DatabaseBackendAsync::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
     44{
     45    DatabaseTaskSynchronizer synchronizer;
     46    if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
     47        return false;
     48
     49#if PLATFORM(CHROMIUM)
     50    DatabaseTracker::tracker().prepareToOpenDatabase(this);
     51#endif
     52    bool success = false;
     53    OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
     54    databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
     55    synchronizer.waitForTaskCompletion();
     56
     57    return success;
     58}
     59
     60bool DatabaseBackendAsync::performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
     61{
     62    if (DatabaseBackend::performOpenAndVerify(setVersionInNewDatabase, error, errorMessage)) {
     63        if (databaseContext()->databaseThread())
     64            databaseContext()->databaseThread()->recordDatabaseOpen(this);
     65
     66        return true;
     67    }
     68
     69    return false;
     70}
    4071
    4172} // namespace WebCore
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h

    r141956 r142030  
    4444
    4545class DatabaseBackendAsync : public DatabaseBackend {
    46 protected:
     46public:
    4747    DatabaseBackendAsync(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
    4848
    49     friend class DatabaseManager; // FIXME: remove this once we have isolated this to the backend.
    50     friend class DatabaseServer;
     49    virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
     50    virtual bool performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
    5151
    5252private:
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp

    r141950 r142030  
    3030
    3131#include "DatabaseBackendContext.h"
     32#include "DatabaseTracker.h"
    3233
    3334namespace WebCore {
     
    5253}
    5354
     55bool DatabaseBackendSync::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
     56{
     57#if PLATFORM(CHROMIUM)
     58    DatabaseTracker::tracker().prepareToOpenDatabase(this);
     59#endif
     60    return performOpenAndVerify(setVersionInNewDatabase, error, errorMessage);
     61}
     62
    5463} // namespace WebCore
    5564
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h

    r141928 r142030  
    4646    virtual ~DatabaseBackendSync();
    4747
     48    virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
     49
    4850protected:
    4951    DatabaseBackendSync(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
    5052
    51     friend class DatabaseManager; // FIXME: remove this once we have isolated this to the backend.
    5253    friend class DatabaseServer;
    5354};
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseError.h

    r141950 r142030  
    3535ENUM_CLASS(DatabaseError) {
    3636    None = 0,
    37     CannotOpenDatabase,
    3837    DatabaseIsBeingDeleted,
    3938    DatabaseSizeExceededQuota,
    40     DatabaseSizeOverflowed
     39    DatabaseSizeOverflowed,
     40    GenericSecurityError,
     41    InvalidDatabaseState
    4142} ENUM_CLASS_END(DatabaseError);
    4243
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp

    r141928 r142030  
    2929#if ENABLE(SQL_DATABASE)
    3030
     31#include "AbstractDatabaseServer.h"
    3132#include "Database.h"
    3233#include "DatabaseBackend.h"
     
    201202#endif
    202203
     204ExceptionCode DatabaseManager::exceptionCodeForDatabaseError(DatabaseError error)
     205{
     206    switch (error) {
     207    case DatabaseError::None:
     208        return 0;
     209    case DatabaseError::DatabaseIsBeingDeleted:
     210    case DatabaseError::DatabaseSizeExceededQuota:
     211    case DatabaseError::DatabaseSizeOverflowed:
     212    case DatabaseError::GenericSecurityError:
     213        return SECURITY_ERR;
     214    case DatabaseError::InvalidDatabaseState:
     215        return INVALID_STATE_ERR;
     216    }
     217    ASSERT_NOT_REACHED();
     218    return 0; // Make some older compilers happy.
     219}
     220
     221static void logOpenDatabaseError(ScriptExecutionContext* context, const String& name)
     222{
     223    LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(),
     224        context->securityOrigin()->toString().ascii().data());
     225}
     226
     227PassRefPtr<DatabaseBackend> DatabaseManager::openDatabaseBackend(ScriptExecutionContext* context,
     228    DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
     229    unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
     230{
     231    ASSERT(error == DatabaseError::None);
     232
     233    RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
     234    RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend();
     235
     236    RefPtr<DatabaseBackend> backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
     237        displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
     238
     239    if (!backend) {
     240        ASSERT(error != DatabaseError::None);
     241
     242        switch (error) {
     243        case DatabaseError::DatabaseIsBeingDeleted:
     244        case DatabaseError::DatabaseSizeOverflowed:
     245        case DatabaseError::GenericSecurityError:
     246            logOpenDatabaseError(context, name);
     247            return 0;
     248
     249        case DatabaseError::InvalidDatabaseState:
     250            logErrorMessage(context, errorMessage);
     251            return 0;
     252
     253        case DatabaseError::DatabaseSizeExceededQuota:
     254            // Notify the client that we've exceeded the database quota.
     255            // The client may want to increase the quota, and we'll give it
     256            // one more try after if that is the case.
     257            databaseContext->databaseExceededQuota(name,
     258                DatabaseDetails(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0));
     259
     260            error = DatabaseError::None;
     261
     262            backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
     263                displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage,
     264                AbstractDatabaseServer::RetryOpenDatabase);
     265            break;
     266
     267        default:
     268            ASSERT_NOT_REACHED();
     269        }
     270
     271        if (!backend) {
     272            ASSERT(error != DatabaseError::None);
     273
     274            if (error == DatabaseError::InvalidDatabaseState) {
     275                logErrorMessage(context, errorMessage);
     276                return 0;
     277            }
     278
     279            logOpenDatabaseError(context, name);
     280            return 0;
     281        }
     282    }
     283
     284    return backend.release();
     285}
     286
    203287PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context,
    204288    const String& name, const String& expectedVersion, const String& displayName,
     
    207291{
    208292    ScriptController::initializeThreading();
     293    ASSERT(error == DatabaseError::None);
     294
     295    bool setVersionInNewDatabase = !creationCallback;
     296    String errorMessage;
     297    RefPtr<DatabaseBackend> backend = openDatabaseBackend(context, DatabaseType::Async, name,
     298        expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
     299    if (!backend)
     300        return 0;
     301
     302    RefPtr<Database> database = Database::create(context, backend);
     303
    209304    RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
    210     RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend();
    211     ASSERT(error == DatabaseError::None);
    212 
    213     if (!m_server->canEstablishDatabase(backendContext.get(), name, displayName, estimatedSize)) {
    214         LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data());
    215         return 0;
    216     }
    217 
    218     String errorMessage;
    219     RefPtr<Database> database = adoptRef(new Database(backendContext, name, expectedVersion, displayName, estimatedSize));
    220 
    221     if (!database->openAndVerifyVersion(!creationCallback, error, errorMessage)) {
    222         logErrorMessage(context, errorMessage);
    223         return 0;
    224     }
    225 
    226     m_server->setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
    227305    databaseContext->setHasOpenDatabases();
    228 
    229306    InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
    230307
    231     if (database->isNew() && creationCallback.get()) {
     308    if (backend->isNew() && creationCallback.get()) {
    232309        LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get());
    233310        database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback));
    234311    }
    235312
     313    ASSERT(database);
    236314    return database.release();
    237315}
     
    241319    unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, DatabaseError& error)
    242320{
    243     RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
    244     RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend();
    245321    ASSERT(context->isContextThread());
    246322    ASSERT(error == DatabaseError::None);
    247323
    248     if (!m_server->canEstablishDatabase(backendContext.get(), name, displayName, estimatedSize)) {
    249         LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data());
     324    bool setVersionInNewDatabase = !creationCallback;
     325    String errorMessage;
     326    RefPtr<DatabaseBackend> backend = openDatabaseBackend(context, DatabaseType::Sync, name,
     327        expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
     328    if (!backend)
    250329        return 0;
    251     }
    252 
    253     String errorMessage;
    254     RefPtr<DatabaseSync> database = adoptRef(new DatabaseSync(backendContext, name, expectedVersion, displayName, estimatedSize));
    255 
    256     if (!database->openAndVerifyVersion(!creationCallback, error, errorMessage)) {
    257         logErrorMessage(context, errorMessage);
    258         return 0;
    259     }
    260 
    261     m_server->setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
    262 
    263     if (database->isNew() && creationCallback.get()) {
     330
     331    RefPtr<DatabaseSync> database = DatabaseSync::create(context, backend);
     332
     333    if (backend->isNew() && creationCallback.get()) {
    264334        LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get());
    265335        creationCallback->handleEvent(database.get());
    266336    }
    267337
     338    ASSERT(database);
    268339    return database.release();
    269340}
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.h

    r141928 r142030  
    2929#if ENABLE(SQL_DATABASE)
    3030
    31 #include "AbstractDatabaseServer.h"
    3231#include "DatabaseBasicTypes.h"
    3332#include "DatabaseDetails.h"
     
    8180#endif
    8281
     82    static ExceptionCode exceptionCodeForDatabaseError(DatabaseError);
     83
    8384    PassRefPtr<Database> openDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, DatabaseError&);
    8485    PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, DatabaseError&);
     
    125126    PassRefPtr<DatabaseContext> existingDatabaseContextFor(ScriptExecutionContext*);
    126127
     128    PassRefPtr<DatabaseBackend> openDatabaseBackend(ScriptExecutionContext*,
     129        DatabaseType, const String& name, const String& expectedVersion, const String& displayName,
     130        unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
     131
    127132    static void logErrorMessage(ScriptExecutionContext*, const String& message);
    128133
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp

    r141928 r142030  
    2929#if ENABLE(SQL_DATABASE)
    3030
     31#include "Database.h"
    3132#include "DatabaseBackendAsync.h"
    3233#include "DatabaseBackendContext.h"
    3334#include "DatabaseBackendSync.h"
     35#include "DatabaseSync.h"
    3436#include "DatabaseTracker.h"
    3537#include <wtf/UnusedParam.h>
     
    150152}
    151153
    152 bool DatabaseServer::canEstablishDatabase(DatabaseBackendContext* backendContext, const String& name, const String& displayName, unsigned long estimatedSize)
    153 {
    154     return DatabaseTracker::tracker().canEstablishDatabase(backendContext, name, displayName, estimatedSize);
    155 }
    156 
    157 void DatabaseServer::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
    158 {
    159     DatabaseTracker::tracker().setDatabaseDetails(origin, name, displayName, estimatedSize);
     154PassRefPtr<DatabaseBackend> DatabaseServer::openDatabase(RefPtr<DatabaseBackendContext>& backendContext,
     155    DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
     156    unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError &error, String& errorMessage,
     157    OpenAttempt attempt)
     158{
     159    RefPtr<DatabaseBackend> database;
     160    bool success = false; // Make some older compilers happy.
     161   
     162    switch (attempt) {
     163    case FirstTryToOpenDatabase:
     164        success = DatabaseTracker::tracker().canEstablishDatabase(backendContext.get(), name, displayName, estimatedSize, error);
     165        break;
     166    case RetryOpenDatabase:
     167        success = DatabaseTracker::tracker().retryCanEstablishDatabase(backendContext.get(), name, displayName, estimatedSize, error);
     168    }
     169
     170    if (success)
     171        database = createDatabase(backendContext, type, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
     172    return database.release();
     173}
     174
     175PassRefPtr<DatabaseBackend> DatabaseServer::createDatabase(RefPtr<DatabaseBackendContext>& backendContext,
     176    DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
     177    unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
     178{
     179    RefPtr<DatabaseBackend> database;
     180    switch (type) {
     181    case DatabaseType::Async:
     182        database = adoptRef(new Database(backendContext, name, expectedVersion, displayName, estimatedSize));
     183        break;
     184    case DatabaseType::Sync:
     185        database = adoptRef(new DatabaseSync(backendContext, name, expectedVersion, displayName, estimatedSize));
     186    }
     187
     188    if (!database->openAndVerifyVersion(setVersionInNewDatabase, error, errorMessage))
     189        return 0;
     190
     191    DatabaseTracker::tracker().setDatabaseDetails(backendContext->securityOrigin(), name, displayName, estimatedSize);
     192    return database.release();
    160193}
    161194
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseServer.h

    r141928 r142030  
    4646    virtual String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist);
    4747
     48    virtual PassRefPtr<DatabaseBackend> openDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError&, String& errorMessage, OpenAttempt);
     49
    4850#if !PLATFORM(CHROMIUM)
    4951    virtual bool hasEntryForOrigin(SecurityOrigin*);
     
    7173    virtual void interruptAllDatabasesForContext(const DatabaseBackendContext*);
    7274
    73     virtual bool canEstablishDatabase(DatabaseBackendContext*, const String& name, const String& displayName, unsigned long estimatedSize);
     75    virtual unsigned long long getMaxSizeForDatabase(const DatabaseBackend*);
    7476
    75     virtual void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize);
    76     virtual unsigned long long getMaxSizeForDatabase(const DatabaseBackend*);
     77protected:
     78    virtual PassRefPtr<DatabaseBackend> createDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
    7779};
    7880
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp

    r141928 r142030  
    5252namespace WebCore {
    5353
     54PassRefPtr<DatabaseSync> DatabaseSync::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackend> backend)
     55{
     56    return static_cast<DatabaseSync*>(backend.get());
     57}
     58
    5459DatabaseSync::DatabaseSync(PassRefPtr<DatabaseBackendContext> databaseContext,
    5560    const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
     
    155160}
    156161
    157 bool DatabaseSync::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
    158 {
    159 #if PLATFORM(CHROMIUM)
    160     DatabaseTracker::tracker().prepareToOpenDatabase(this);
    161 #endif
    162     return performOpenAndVerify(setVersionInNewDatabase, error, errorMessage);
    163 }
    164 
    165162void DatabaseSync::markAsDeletedAndClose()
    166163{
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.h

    r141928 r142030  
    7878        const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
    7979    PassRefPtr<DatabaseBackendSync> backend();
     80    static PassRefPtr<DatabaseSync> create(ScriptExecutionContext*, PassRefPtr<DatabaseBackend>);
    8081
    8182    void runTransaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&);
    82 
    83     bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
    8483
    8584    String m_lastErrorMessage;
    8685
    8786    friend class DatabaseManager;
    88     friend class DatabaseBackendSync; // FIXME: remove this when the backend has been split out.
     87    friend class DatabaseServer; // FIXME: remove this when the backend has been split out.
    8988};
    9089
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp

    r141928 r142030  
    143143}
    144144
    145 bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, const String& name, const String& displayName, unsigned long estimatedSize)
    146 {
    147     SecurityOrigin* origin = context->securityOrigin();
    148     unsigned long long requirement;
    149     {
    150         MutexLocker lockDatabase(m_databaseGuard);
    151         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    152 
    153         if (isDeletingDatabaseOrOriginFor(origin, name))
    154             return false;
    155 
    156         recordCreatingDatabase(origin, name);
    157 
    158         // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker
    159         // by fetching its current usage now.
    160         unsigned long long usage = usageForOriginNoLock(origin);
    161 
    162         // If a database already exists, ignore the passed-in estimated size and say it's OK.
    163         if (hasEntryForDatabase(origin, name))
    164             return true;
    165 
    166         // If the database will fit, allow its creation.
    167         requirement = usage + max(1UL, estimatedSize);
    168         if (requirement < usage) {
    169             doneCreatingDatabase(origin, name);
    170             return false; // If the estimated size is so big it causes an overflow, don't allow creation.
    171         }
    172         if (requirement <= quotaForOriginNoLock(origin))
    173             return true;
    174     }
    175 
    176     // Give the chrome client a chance to increase the quota.
    177     // Drop all locks before calling out; we don't know what they'll do.
    178     context->databaseExceededQuota(name, DatabaseDetails(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0));
    179 
    180     MutexLocker lockDatabase(m_databaseGuard);
    181 
    182     // If the database will fit now, allow its creation.
     145bool DatabaseTracker::hasAdequateQuotaForOrigin(SecurityOrigin* origin, unsigned long estimatedSize, DatabaseError& err)
     146{
     147    // Since we're imminently opening a database within this context's origin,
     148    // make sure this origin is being tracked by the OriginQuotaManager
     149    // by fetching its current usage now. Calling usageForOrigin() has the side
     150    // effect of initiating tracking by the OriginQuotaManager if the origin is
     151    // not already tracked.
     152    unsigned long long usage = usageForOriginNoLock(origin);
     153
     154    // If the database will fit, allow its creation.
     155    unsigned long long requirement = usage + max(1UL, estimatedSize);
     156    if (requirement < usage) {
     157        // The estimated size is so big it causes an overflow; don't allow creation.
     158        err = DatabaseError::DatabaseSizeOverflowed;
     159        return false;
     160    }
    183161    if (requirement <= quotaForOriginNoLock(origin))
    184162        return true;
    185163
     164    err = DatabaseError::DatabaseSizeExceededQuota;
     165    return false;
     166}
     167
     168bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error)
     169{
     170    UNUSED_PARAM(displayName); // Chromium needs the displayName but we don't.
     171    error = DatabaseError::None;
     172
     173    MutexLocker lockDatabase(m_databaseGuard);
     174    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
     175    SecurityOrigin* origin = context->securityOrigin();
     176
     177    if (isDeletingDatabaseOrOriginFor(origin, name)) {
     178        error = DatabaseError::DatabaseIsBeingDeleted;
     179        return false;
     180    }
     181
     182    recordCreatingDatabase(origin, name);
     183
     184    // If a database already exists, ignore the passed-in estimated size and say it's OK.
     185    if (hasEntryForDatabase(origin, name))
     186        return true;
     187
     188    if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
     189        ASSERT(error == DatabaseError::None);
     190        return true;
     191    }
     192
     193    // If we get here, then we do not have enough quota for one of the
     194    // following reasons as indicated by the set error:
     195    //
     196    // If the error is DatabaseSizeOverflowed, then this means the requested
     197    // estimatedSize if so unreasonably large that it can cause an overflow in
     198    // the usage budget computation. In that case, there's nothing more we can
     199    // do, and there's no need for a retry. Hence, we should indicate that
     200    // we're done with our attempt to create the database.
     201    //
     202    // If the error is DatabaseSizeExceededQuota, then we'll give the client
     203    // a chance to update the quota and call retryCanEstablishDatabase() to try
     204    // again. Hence, we don't call doneCreatingDatabase() yet in that case.
     205
     206    if (error == DatabaseError::DatabaseSizeOverflowed)
     207        doneCreatingDatabase(origin, name);
     208    else
     209        ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
     210
     211    return false;
     212}
     213
     214// Note: a thought about performance: hasAdequateQuotaForOrigin() was also
     215// called in canEstablishDatabase(), and hence, we're repeating some work within
     216// hasAdequateQuotaForOrigin(). However, retryCanEstablishDatabase() should only
     217// be called in the rare even if canEstablishDatabase() fails. Since it is rare,
     218// we should not bother optimizing it. It is more beneficial to keep
     219// hasAdequateQuotaForOrigin() simple and correct (i.e. bug free), and just
     220// re-use it. Also note that the path for opening a database involves IO, and
     221// hence should not be a performance critical path anyway.
     222bool DatabaseTracker::retryCanEstablishDatabase(DatabaseBackendContext* context, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error)
     223{
     224    // Chromium needs the displayName in canEstablishDatabase(), but we don't.
     225    // Though Chromium does not use retryCanEstablishDatabase(), we should
     226    // keep the prototypes for canEstablishDatabase() and its retry function
     227    // the same. Hence, we also have an unneeded displayName arg here.
     228    UNUSED_PARAM(displayName);
     229    error = DatabaseError::None;
     230
     231    MutexLocker lockDatabase(m_databaseGuard);
     232    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
     233    SecurityOrigin* origin = context->securityOrigin();
     234
     235    // We have already eliminated other types of errors in canEstablishDatabase().
     236    // The only reason we're in retryCanEstablishDatabase() is because we gave
     237    // the client a chance to update the quota and are rechecking it here.
     238    // If we fail this check, the only possible reason this time should be due
     239    // to inadequate quota.
     240    if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
     241        ASSERT(error == DatabaseError::None);
     242        return true;
     243    }
     244
     245    ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
    186246    doneCreatingDatabase(origin, name);
    187247
  • trunk/Source/WebCore/Modules/webdatabase/DatabaseTracker.h

    r141928 r142030  
    3232#if ENABLE(SQL_DATABASE)
    3333
     34#include "DatabaseError.h"
    3435#include <wtf/HashMap.h>
    3536#include <wtf/HashSet.h>
     
    7172    // notificationMutex() is currently independent of the other locks.
    7273
    73     bool canEstablishDatabase(DatabaseBackendContext*, const String& name, const String& displayName, unsigned long estimatedSize);
     74    bool canEstablishDatabase(DatabaseBackendContext*, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError&);
     75    bool retryCanEstablishDatabase(DatabaseBackendContext*, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError&);
     76
    7477    void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize);
    7578    String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true);
     
    8689private:
    8790    explicit DatabaseTracker(const String& databasePath);
     91
     92    bool hasAdequateQuotaForOrigin(SecurityOrigin*, unsigned long estimatedSize, DatabaseError&);
    8893
    8994#if !PLATFORM(CHROMIUM)
  • trunk/Source/WebCore/Modules/webdatabase/WorkerContextWebDatabase.cpp

    r141668 r142030  
    4646    RefPtr<Database> database;
    4747    DatabaseError error = DatabaseError::None;
    48     if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin()))
     48    if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
    4949        database = dbManager.openDatabase(context, name, version, displayName, estimatedSize, creationCallback, error);
    50 
    51     ASSERT(error == DatabaseError::None || error == DatabaseError::CannotOpenDatabase);
    52     if (error == DatabaseError::CannotOpenDatabase)
    53         ec = INVALID_STATE_ERR;
    54     if (!database && !ec)
     50        ASSERT(database || error != DatabaseError::None);
     51        ec = DatabaseManager::exceptionCodeForDatabaseError(error);
     52    } else
    5553        ec = SECURITY_ERR;
    5654
     
    6361    RefPtr<DatabaseSync> database;
    6462    DatabaseError error =  DatabaseError::None;
    65     if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin()))
     63    if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
    6664        database = dbManager.openDatabaseSync(context, name, version, displayName, estimatedSize, creationCallback, error);
    6765
    68     ASSERT(error == DatabaseError::None || error == DatabaseError::CannotOpenDatabase);
    69     if (error == DatabaseError::CannotOpenDatabase)
    70         ec = INVALID_STATE_ERR;
    71     if (!database && !ec)
     66        ASSERT(database || error != DatabaseError::None);
     67        ec = DatabaseManager::exceptionCodeForDatabaseError(error);
     68    } else
    7269        ec = SECURITY_ERR;
    7370
  • trunk/Source/WebCore/Modules/webdatabase/chromium/DatabaseTrackerChromium.cpp

    r141928 r142030  
    4242#include "SecurityOriginHash.h"
    4343#include "SQLiteFileSystem.h"
     44#include <wtf/Assertions.h>
    4445#include <wtf/StdLibExtras.h>
    4546#include <wtf/text/WTFString.h>
     
    5859}
    5960
    60 bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* databaseContext, const String& name, const String& displayName, unsigned long estimatedSize)
     61bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* databaseContext, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error)
    6162{
    6263    ScriptExecutionContext* scriptExecutionContext = databaseContext->scriptExecutionContext();
    63     return DatabaseObserver::canEstablishDatabase(scriptExecutionContext, name, displayName, estimatedSize);
     64    bool success = DatabaseObserver::canEstablishDatabase(scriptExecutionContext, name, displayName, estimatedSize);
     65    if (!success)
     66        error = DatabaseError::GenericSecurityError;
     67    return success;
     68}
     69
     70bool DatabaseTracker::retryCanEstablishDatabase(DatabaseBackendContext*, const String&, const String&, unsigned long, DatabaseError&)
     71{
     72    ASSERT_NOT_REACHED();
     73    return false;
    6474}
    6575
Note: See TracChangeset for help on using the changeset viewer.