Home | History | Annotate | Download | only in storage
      1 /*
      2  * Copyright (C) 2011 Google 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "IDBDatabaseBackendImpl.h"
     28 
     29 #if ENABLE(INDEXED_DATABASE)
     30 
     31 #include "CrossThreadTask.h"
     32 #include "DOMStringList.h"
     33 #include "IDBBackingStore.h"
     34 #include "IDBDatabaseException.h"
     35 #include "IDBFactoryBackendImpl.h"
     36 #include "IDBObjectStoreBackendImpl.h"
     37 #include "IDBTransactionBackendImpl.h"
     38 #include "IDBTransactionCoordinator.h"
     39 
     40 namespace WebCore {
     41 
     42 class IDBDatabaseBackendImpl::PendingSetVersionCall : public RefCounted<PendingSetVersionCall> {
     43 public:
     44     static PassRefPtr<PendingSetVersionCall> create(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
     45     {
     46         return adoptRef(new PendingSetVersionCall(version, callbacks, databaseCallbacks));
     47     }
     48     String version() { return m_version; }
     49     PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
     50     PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
     51 
     52 private:
     53     PendingSetVersionCall(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
     54         : m_version(version)
     55         , m_callbacks(callbacks)
     56         , m_databaseCallbacks(databaseCallbacks)
     57     {
     58     }
     59     String m_version;
     60     RefPtr<IDBCallbacks> m_callbacks;
     61     RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
     62 };
     63 
     64 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
     65     : m_backingStore(backingStore)
     66     , m_id(InvalidId)
     67     , m_name(name)
     68     , m_version("")
     69     , m_identifier(uniqueIdentifier)
     70     , m_factory(factory)
     71     , m_transactionCoordinator(coordinator)
     72 {
     73     ASSERT(!m_name.isNull());
     74 
     75     bool success = m_backingStore->extractIDBDatabaseMetaData(m_name, m_version, m_id);
     76     ASSERT_UNUSED(success, success == (m_id != InvalidId));
     77     if (!m_backingStore->setIDBDatabaseMetaData(m_name, m_version, m_id, m_id == InvalidId))
     78         ASSERT_NOT_REACHED(); // FIXME: Need better error handling.
     79     loadObjectStores();
     80 }
     81 
     82 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
     83 {
     84     m_factory->removeIDBDatabaseBackend(m_identifier);
     85 }
     86 
     87 PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
     88 {
     89     return m_backingStore;
     90 }
     91 
     92 PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const
     93 {
     94     RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
     95     for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it)
     96         objectStoreNames->append(it->first);
     97     return objectStoreNames.release();
     98 }
     99 
    100 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
    101 {
    102     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
    103 
    104     if (m_objectStores.contains(name)) {
    105         ec = IDBDatabaseException::CONSTRAINT_ERR;
    106         return 0;
    107     }
    108 
    109     RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, name, keyPath, autoIncrement);
    110     ASSERT(objectStore->name() == name);
    111 
    112     RefPtr<IDBDatabaseBackendImpl> database = this;
    113     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
    114     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::createObjectStoreInternal, database, objectStore, transaction),
    115                                    createCallbackTask(&IDBDatabaseBackendImpl::removeObjectStoreFromMap, database, objectStore))) {
    116         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
    117         return 0;
    118     }
    119 
    120     m_objectStores.set(name, objectStore);
    121     return objectStore.release();
    122 }
    123 
    124 void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore,  PassRefPtr<IDBTransactionBackendInterface> transaction)
    125 {
    126     int64_t objectStoreId;
    127 
    128     if (!database->m_backingStore->createObjectStore(database->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), objectStoreId)) {
    129         transaction->abort();
    130         return;
    131     }
    132 
    133     objectStore->setId(objectStoreId);
    134     transaction->didCompleteTaskEvents();
    135 }
    136 
    137 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name)
    138 {
    139     return m_objectStores.get(name);
    140 }
    141 
    142 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
    143 {
    144     RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
    145     if (!objectStore) {
    146         ec = IDBDatabaseException::NOT_FOUND_ERR;
    147         return;
    148     }
    149     RefPtr<IDBDatabaseBackendImpl> database = this;
    150     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
    151     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction),
    152                                    createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) {
    153         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
    154         return;
    155     }
    156     m_objectStores.remove(name);
    157 }
    158 
    159 void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
    160 {
    161     database->m_backingStore->deleteObjectStore(database->id(), objectStore->id());
    162     transaction->didCompleteTaskEvents();
    163 }
    164 
    165 void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, ExceptionCode& ec)
    166 {
    167     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
    168     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
    169     if (!m_databaseCallbacksSet.contains(databaseCallbacks)) {
    170         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "Connection was closed before set version transaction was created"));
    171         return;
    172     }
    173     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
    174         if (*it != databaseCallbacks)
    175             (*it)->onVersionChange(version);
    176     }
    177     if (m_databaseCallbacksSet.size() > 1) {
    178         callbacks->onBlocked();
    179         RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks);
    180         m_pendingSetVersionCalls.append(pendingSetVersionCall);
    181         return;
    182     }
    183 
    184     RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
    185     RefPtr<IDBDatabaseBackendImpl> database = this;
    186     RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, this);
    187     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction),
    188                                    createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) {
    189         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
    190     }
    191 }
    192 
    193 void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
    194 {
    195     int64_t databaseId = database->id();
    196     database->m_version = version;
    197     if (!database->m_backingStore->setIDBDatabaseMetaData(database->m_name, database->m_version, databaseId, databaseId == InvalidId)) {
    198         // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
    199         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
    200         transaction->abort();
    201         return;
    202     }
    203     callbacks->onSuccess(transaction);
    204 }
    205 
    206 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec)
    207 {
    208     for (size_t i = 0; i < objectStoreNames->length(); ++i) {
    209         if (!m_objectStores.contains(objectStoreNames->item(i))) {
    210             ec = IDBDatabaseException::NOT_FOUND_ERR;
    211             return 0;
    212         }
    213     }
    214 
    215     // FIXME: Return not allowed err if close has been called.
    216     return IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
    217 }
    218 
    219 void IDBDatabaseBackendImpl::open(PassRefPtr<IDBDatabaseCallbacks> callbacks)
    220 {
    221     m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(callbacks));
    222 }
    223 
    224 void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
    225 {
    226     RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
    227     ASSERT(m_databaseCallbacksSet.contains(callbacks));
    228     m_databaseCallbacksSet.remove(callbacks);
    229     if (m_databaseCallbacksSet.size() > 1)
    230         return;
    231 
    232     while (!m_pendingSetVersionCalls.isEmpty()) {
    233         ExceptionCode ec = 0;
    234         RefPtr<PendingSetVersionCall> pendingSetVersionCall = m_pendingSetVersionCalls.takeFirst();
    235         setVersion(pendingSetVersionCall->version(), pendingSetVersionCall->callbacks(), pendingSetVersionCall->databaseCallbacks(), ec);
    236         ASSERT(!ec);
    237     }
    238 }
    239 
    240 void IDBDatabaseBackendImpl::loadObjectStores()
    241 {
    242     Vector<int64_t> ids;
    243     Vector<String> names;
    244     Vector<String> keyPaths;
    245     Vector<bool> autoIncrementFlags;
    246     m_backingStore->getObjectStores(m_id, ids, names, keyPaths, autoIncrementFlags);
    247 
    248     ASSERT(names.size() == ids.size());
    249     ASSERT(keyPaths.size() == ids.size());
    250     ASSERT(autoIncrementFlags.size() == ids.size());
    251 
    252     for (size_t i = 0; i < ids.size(); i++)
    253         m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, ids[i], names[i], keyPaths[i], autoIncrementFlags[i]));
    254 }
    255 
    256 void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
    257 {
    258     ASSERT(database->m_objectStores.contains(objectStore->name()));
    259     database->m_objectStores.remove(objectStore->name());
    260 }
    261 
    262 void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
    263 {
    264     RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
    265     ASSERT(!database->m_objectStores.contains(objectStorePtr->name()));
    266     database->m_objectStores.set(objectStorePtr->name(), objectStorePtr);
    267 }
    268 
    269 void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version)
    270 {
    271     database->m_version = version;
    272 }
    273 
    274 
    275 } // namespace WebCore
    276 
    277 #endif // ENABLE(INDEXED_DATABASE)
    278