diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp b/chromium/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp new file mode 100644 index 00000000000..17213cf94af --- /dev/null +++ b/chromium/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp @@ -0,0 +1,777 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "modules/indexeddb/InspectorIndexedDBAgent.h" + +#include "bindings/v8/ExceptionState.h" +#include "bindings/v8/ExceptionStatePlaceholder.h" +#include "bindings/v8/ScriptController.h" +#include "bindings/v8/ScriptState.h" +#include "core/dom/DOMStringList.h" +#include "core/dom/Document.h" +#include "core/events/EventListener.h" +#include "core/frame/LocalFrame.h" +#include "core/inspector/InspectorController.h" +#include "core/inspector/InspectorState.h" +#include "core/page/Page.h" +#include "modules/indexeddb/DOMWindowIndexedDatabase.h" +#include "modules/indexeddb/IDBCursor.h" +#include "modules/indexeddb/IDBCursorWithValue.h" +#include "modules/indexeddb/IDBDatabase.h" +#include "modules/indexeddb/IDBFactory.h" +#include "modules/indexeddb/IDBIndex.h" +#include "modules/indexeddb/IDBKey.h" +#include "modules/indexeddb/IDBKeyPath.h" +#include "modules/indexeddb/IDBKeyRange.h" +#include "modules/indexeddb/IDBMetadata.h" +#include "modules/indexeddb/IDBObjectStore.h" +#include "modules/indexeddb/IDBOpenDBRequest.h" +#include "modules/indexeddb/IDBPendingTransactionMonitor.h" +#include "modules/indexeddb/IDBRequest.h" +#include "modules/indexeddb/IDBTransaction.h" +#include "platform/JSONValues.h" +#include "platform/weborigin/SecurityOrigin.h" +#include "public/platform/WebIDBCursor.h" +#include "public/platform/WebIDBTypes.h" +#include "wtf/Vector.h" + +using WebCore::TypeBuilder::Array; +using WebCore::TypeBuilder::IndexedDB::DatabaseWithObjectStores; +using WebCore::TypeBuilder::IndexedDB::DataEntry; +using WebCore::TypeBuilder::IndexedDB::Key; +using WebCore::TypeBuilder::IndexedDB::KeyPath; +using WebCore::TypeBuilder::IndexedDB::KeyRange; +using WebCore::TypeBuilder::IndexedDB::ObjectStore; +using WebCore::TypeBuilder::IndexedDB::ObjectStoreIndex; + +typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::RequestDatabaseNamesCallback RequestDatabaseNamesCallback; +typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::RequestDatabaseCallback RequestDatabaseCallback; +typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::RequestDataCallback RequestDataCallback; +typedef WebCore::InspectorBackendDispatcher::CallbackBase RequestCallback; +typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::ClearObjectStoreCallback ClearObjectStoreCallback; + +namespace WebCore { + +namespace IndexedDBAgentState { +static const char indexedDBAgentEnabled[] = "indexedDBAgentEnabled"; +}; + +namespace { + +class GetDatabaseNamesCallback FINAL : public EventListener { + WTF_MAKE_NONCOPYABLE(GetDatabaseNamesCallback); +public: + static PassRefPtr<GetDatabaseNamesCallback> create(PassRefPtr<RequestDatabaseNamesCallback> requestCallback, const String& securityOrigin) + { + return adoptRef(new GetDatabaseNamesCallback(requestCallback, securityOrigin)); + } + + virtual ~GetDatabaseNamesCallback() { } + + virtual bool operator==(const EventListener& other) OVERRIDE + { + return this == &other; + } + + virtual void handleEvent(ExecutionContext*, Event* event) OVERRIDE + { + if (!m_requestCallback->isActive()) + return; + if (event->type() != EventTypeNames::success) { + m_requestCallback->sendFailure("Unexpected event type."); + return; + } + + IDBRequest* idbRequest = static_cast<IDBRequest*>(event->target()); + IDBAny* requestResult = idbRequest->resultAsAny(); + if (requestResult->type() != IDBAny::DOMStringListType) { + m_requestCallback->sendFailure("Unexpected result type."); + return; + } + + RefPtrWillBeRawPtr<DOMStringList> databaseNamesList = requestResult->domStringList(); + RefPtr<TypeBuilder::Array<String> > databaseNames = TypeBuilder::Array<String>::create(); + for (size_t i = 0; i < databaseNamesList->length(); ++i) + databaseNames->addItem(databaseNamesList->item(i)); + m_requestCallback->sendSuccess(databaseNames.release()); + } + +private: + GetDatabaseNamesCallback(PassRefPtr<RequestDatabaseNamesCallback> requestCallback, const String& securityOrigin) + : EventListener(EventListener::CPPEventListenerType) + , m_requestCallback(requestCallback) + , m_securityOrigin(securityOrigin) { } + RefPtr<RequestDatabaseNamesCallback> m_requestCallback; + String m_securityOrigin; +}; + +class ExecutableWithDatabase : public RefCounted<ExecutableWithDatabase> { +public: + ExecutableWithDatabase(ScriptState* scriptState) + : m_scriptState(scriptState) { } + virtual ~ExecutableWithDatabase() { }; + void start(IDBFactory*, SecurityOrigin*, const String& databaseName); + virtual void execute(IDBDatabase*) = 0; + virtual RequestCallback* requestCallback() = 0; + ExecutionContext* context() const { return m_scriptState->executionContext(); } + ScriptState* scriptState() const { return m_scriptState.get(); } +private: + RefPtr<ScriptState> m_scriptState; +}; + +class OpenDatabaseCallback FINAL : public EventListener { +public: + static PassRefPtr<OpenDatabaseCallback> create(ExecutableWithDatabase* executableWithDatabase) + { + return adoptRef(new OpenDatabaseCallback(executableWithDatabase)); + } + + virtual ~OpenDatabaseCallback() { } + + virtual bool operator==(const EventListener& other) OVERRIDE + { + return this == &other; + } + + virtual void handleEvent(ExecutionContext* context, Event* event) OVERRIDE + { + if (event->type() != EventTypeNames::success) { + m_executableWithDatabase->requestCallback()->sendFailure("Unexpected event type."); + return; + } + + IDBOpenDBRequest* idbOpenDBRequest = static_cast<IDBOpenDBRequest*>(event->target()); + IDBAny* requestResult = idbOpenDBRequest->resultAsAny(); + if (requestResult->type() != IDBAny::IDBDatabaseType) { + m_executableWithDatabase->requestCallback()->sendFailure("Unexpected result type."); + return; + } + + IDBDatabase* idbDatabase = requestResult->idbDatabase(); + m_executableWithDatabase->execute(idbDatabase); + IDBPendingTransactionMonitor::from(*context).deactivateNewTransactions(); + idbDatabase->close(); + } + +private: + OpenDatabaseCallback(ExecutableWithDatabase* executableWithDatabase) + : EventListener(EventListener::CPPEventListenerType) + , m_executableWithDatabase(executableWithDatabase) { } + RefPtr<ExecutableWithDatabase> m_executableWithDatabase; +}; + +void ExecutableWithDatabase::start(IDBFactory* idbFactory, SecurityOrigin*, const String& databaseName) +{ + RefPtr<OpenDatabaseCallback> callback = OpenDatabaseCallback::create(this); + TrackExceptionState exceptionState; + IDBOpenDBRequest* idbOpenDBRequest = idbFactory->open(scriptState(), databaseName, exceptionState); + if (exceptionState.hadException()) { + requestCallback()->sendFailure("Could not open database."); + return; + } + idbOpenDBRequest->addEventListener(EventTypeNames::success, callback, false); +} + +static IDBTransaction* transactionForDatabase(ExecutionContext* executionContext, IDBDatabase* idbDatabase, const String& objectStoreName, const String& mode = IDBTransaction::modeReadOnly()) +{ + TrackExceptionState exceptionState; + IDBTransaction* idbTransaction = idbDatabase->transaction(executionContext, objectStoreName, mode, exceptionState); + if (exceptionState.hadException()) + return 0; + return idbTransaction; +} + +static IDBObjectStore* objectStoreForTransaction(IDBTransaction* idbTransaction, const String& objectStoreName) +{ + TrackExceptionState exceptionState; + IDBObjectStore* idbObjectStore = idbTransaction->objectStore(objectStoreName, exceptionState); + if (exceptionState.hadException()) + return 0; + return idbObjectStore; +} + +static IDBIndex* indexForObjectStore(IDBObjectStore* idbObjectStore, const String& indexName) +{ + TrackExceptionState exceptionState; + IDBIndex* idbIndex = idbObjectStore->index(indexName, exceptionState); + if (exceptionState.hadException()) + return 0; + return idbIndex; +} + +static PassRefPtr<KeyPath> keyPathFromIDBKeyPath(const IDBKeyPath& idbKeyPath) +{ + RefPtr<KeyPath> keyPath; + switch (idbKeyPath.type()) { + case IDBKeyPath::NullType: + keyPath = KeyPath::create().setType(KeyPath::Type::Null); + break; + case IDBKeyPath::StringType: + keyPath = KeyPath::create().setType(KeyPath::Type::String); + keyPath->setString(idbKeyPath.string()); + break; + case IDBKeyPath::ArrayType: { + keyPath = KeyPath::create().setType(KeyPath::Type::Array); + RefPtr<TypeBuilder::Array<String> > array = TypeBuilder::Array<String>::create(); + const Vector<String>& stringArray = idbKeyPath.array(); + for (size_t i = 0; i < stringArray.size(); ++i) + array->addItem(stringArray[i]); + keyPath->setArray(array); + break; + } + default: + ASSERT_NOT_REACHED(); + } + + return keyPath.release(); +} + +class DatabaseLoader FINAL : public ExecutableWithDatabase { +public: + static PassRefPtr<DatabaseLoader> create(ScriptState* scriptState, PassRefPtr<RequestDatabaseCallback> requestCallback) + { + return adoptRef(new DatabaseLoader(scriptState, requestCallback)); + } + + virtual ~DatabaseLoader() { } + + virtual void execute(IDBDatabase* idbDatabase) OVERRIDE + { + if (!requestCallback()->isActive()) + return; + + const IDBDatabaseMetadata databaseMetadata = idbDatabase->metadata(); + + RefPtr<TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStore> > objectStores = TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStore>::create(); + + for (IDBDatabaseMetadata::ObjectStoreMap::const_iterator it = databaseMetadata.objectStores.begin(); it != databaseMetadata.objectStores.end(); ++it) { + const IDBObjectStoreMetadata& objectStoreMetadata = it->value; + + RefPtr<TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStoreIndex> > indexes = TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStoreIndex>::create(); + + for (IDBObjectStoreMetadata::IndexMap::const_iterator it = objectStoreMetadata.indexes.begin(); it != objectStoreMetadata.indexes.end(); ++it) { + const IDBIndexMetadata& indexMetadata = it->value; + + RefPtr<ObjectStoreIndex> objectStoreIndex = ObjectStoreIndex::create() + .setName(indexMetadata.name) + .setKeyPath(keyPathFromIDBKeyPath(indexMetadata.keyPath)) + .setUnique(indexMetadata.unique) + .setMultiEntry(indexMetadata.multiEntry); + indexes->addItem(objectStoreIndex); + } + + RefPtr<ObjectStore> objectStore = ObjectStore::create() + .setName(objectStoreMetadata.name) + .setKeyPath(keyPathFromIDBKeyPath(objectStoreMetadata.keyPath)) + .setAutoIncrement(objectStoreMetadata.autoIncrement) + .setIndexes(indexes); + objectStores->addItem(objectStore); + } + RefPtr<DatabaseWithObjectStores> result = DatabaseWithObjectStores::create() + .setName(databaseMetadata.name) + .setIntVersion(databaseMetadata.intVersion) + .setVersion(databaseMetadata.version) + .setObjectStores(objectStores); + + m_requestCallback->sendSuccess(result); + } + + virtual RequestCallback* requestCallback() OVERRIDE { return m_requestCallback.get(); } +private: + DatabaseLoader(ScriptState* scriptState, PassRefPtr<RequestDatabaseCallback> requestCallback) + : ExecutableWithDatabase(scriptState) + , m_requestCallback(requestCallback) { } + RefPtr<RequestDatabaseCallback> m_requestCallback; +}; + +static IDBKey* idbKeyFromInspectorObject(JSONObject* key) +{ + IDBKey* idbKey; + + String type; + if (!key->getString("type", &type)) + return 0; + + DEFINE_STATIC_LOCAL(String, number, ("number")); + DEFINE_STATIC_LOCAL(String, string, ("string")); + DEFINE_STATIC_LOCAL(String, date, ("date")); + DEFINE_STATIC_LOCAL(String, array, ("array")); + + if (type == number) { + double number; + if (!key->getNumber("number", &number)) + return 0; + idbKey = IDBKey::createNumber(number); + } else if (type == string) { + String string; + if (!key->getString("string", &string)) + return 0; + idbKey = IDBKey::createString(string); + } else if (type == date) { + double date; + if (!key->getNumber("date", &date)) + return 0; + idbKey = IDBKey::createDate(date); + } else if (type == array) { + IDBKey::KeyArray keyArray; + RefPtr<JSONArray> array = key->getArray("array"); + for (size_t i = 0; i < array->length(); ++i) { + RefPtr<JSONValue> value = array->get(i); + RefPtr<JSONObject> object; + if (!value->asObject(&object)) + return 0; + keyArray.append(idbKeyFromInspectorObject(object.get())); + } + idbKey = IDBKey::createArray(keyArray); + } else { + return 0; + } + + return idbKey; +} + +static IDBKeyRange* idbKeyRangeFromKeyRange(JSONObject* keyRange) +{ + RefPtr<JSONObject> lower = keyRange->getObject("lower"); + IDBKey* idbLower = lower ? idbKeyFromInspectorObject(lower.get()) : 0; + if (lower && !idbLower) + return 0; + + RefPtr<JSONObject> upper = keyRange->getObject("upper"); + IDBKey* idbUpper = upper ? idbKeyFromInspectorObject(upper.get()) : 0; + if (upper && !idbUpper) + return 0; + + bool lowerOpen; + if (!keyRange->getBoolean("lowerOpen", &lowerOpen)) + return 0; + IDBKeyRange::LowerBoundType lowerBoundType = lowerOpen ? IDBKeyRange::LowerBoundOpen : IDBKeyRange::LowerBoundClosed; + + bool upperOpen; + if (!keyRange->getBoolean("upperOpen", &upperOpen)) + return 0; + IDBKeyRange::UpperBoundType upperBoundType = upperOpen ? IDBKeyRange::UpperBoundOpen : IDBKeyRange::UpperBoundClosed; + + return IDBKeyRange::create(idbLower, idbUpper, lowerBoundType, upperBoundType); +} + +class DataLoader; + +class OpenCursorCallback FINAL : public EventListener { +public: + static PassRefPtr<OpenCursorCallback> create(ScriptState* scriptState, PassRefPtr<RequestDataCallback> requestCallback, int skipCount, unsigned pageSize) + { + return adoptRef(new OpenCursorCallback(scriptState, requestCallback, skipCount, pageSize)); + } + + virtual ~OpenCursorCallback() { } + + virtual bool operator==(const EventListener& other) OVERRIDE + { + return this == &other; + } + + virtual void handleEvent(ExecutionContext*, Event* event) OVERRIDE + { + if (event->type() != EventTypeNames::success) { + m_requestCallback->sendFailure("Unexpected event type."); + return; + } + + IDBRequest* idbRequest = static_cast<IDBRequest*>(event->target()); + IDBAny* requestResult = idbRequest->resultAsAny(); + if (requestResult->type() == IDBAny::BufferType) { + end(false); + return; + } + if (requestResult->type() != IDBAny::IDBCursorWithValueType) { + m_requestCallback->sendFailure("Unexpected result type."); + return; + } + + IDBCursorWithValue* idbCursor = requestResult->idbCursorWithValue(); + + if (m_skipCount) { + TrackExceptionState exceptionState; + idbCursor->advance(m_skipCount, exceptionState); + if (exceptionState.hadException()) + m_requestCallback->sendFailure("Could not advance cursor."); + m_skipCount = 0; + return; + } + + if (m_result->length() == m_pageSize) { + end(true); + return; + } + + // Continue cursor before making injected script calls, otherwise transaction might be finished. + TrackExceptionState exceptionState; + idbCursor->continueFunction(0, 0, exceptionState); + if (exceptionState.hadException()) { + m_requestCallback->sendFailure("Could not continue cursor."); + return; + } + + Document* document = toDocument(m_scriptState->executionContext()); + if (!document) + return; + RefPtr<DataEntry> dataEntry = DataEntry::create() + .setKey(idbCursor->key(m_scriptState.get()).toJSONValue(m_scriptState.get())->toJSONString()) + .setPrimaryKey(idbCursor->primaryKey(m_scriptState.get()).toJSONValue(m_scriptState.get())->toJSONString()) + .setValue(idbCursor->value(m_scriptState.get()).toJSONValue(m_scriptState.get())->toJSONString()); + m_result->addItem(dataEntry); + + } + + void end(bool hasMore) + { + if (!m_requestCallback->isActive()) + return; + m_requestCallback->sendSuccess(m_result.release(), hasMore); + } + +private: + OpenCursorCallback(ScriptState* scriptState, PassRefPtr<RequestDataCallback> requestCallback, int skipCount, unsigned pageSize) + : EventListener(EventListener::CPPEventListenerType) + , m_scriptState(scriptState) + , m_requestCallback(requestCallback) + , m_skipCount(skipCount) + , m_pageSize(pageSize) + { + m_result = Array<DataEntry>::create(); + } + + RefPtr<ScriptState> m_scriptState; + RefPtr<RequestDataCallback> m_requestCallback; + int m_skipCount; + unsigned m_pageSize; + RefPtr<Array<DataEntry> > m_result; +}; + +class DataLoader FINAL : public ExecutableWithDatabase { +public: + static PassRefPtr<DataLoader> create(ScriptState* scriptState, PassRefPtr<RequestDataCallback> requestCallback, const String& objectStoreName, const String& indexName, IDBKeyRange* idbKeyRange, int skipCount, unsigned pageSize) + { + return adoptRef(new DataLoader(scriptState, requestCallback, objectStoreName, indexName, idbKeyRange, skipCount, pageSize)); + } + + virtual ~DataLoader() { } + + virtual void execute(IDBDatabase* idbDatabase) OVERRIDE + { + if (!requestCallback()->isActive()) + return; + IDBTransaction* idbTransaction = transactionForDatabase(context(), idbDatabase, m_objectStoreName); + if (!idbTransaction) { + m_requestCallback->sendFailure("Could not get transaction"); + return; + } + IDBObjectStore* idbObjectStore = objectStoreForTransaction(idbTransaction, m_objectStoreName); + if (!idbObjectStore) { + m_requestCallback->sendFailure("Could not get object store"); + return; + } + + RefPtr<OpenCursorCallback> openCursorCallback = OpenCursorCallback::create(scriptState(), m_requestCallback, m_skipCount, m_pageSize); + + IDBRequest* idbRequest; + if (!m_indexName.isEmpty()) { + IDBIndex* idbIndex = indexForObjectStore(idbObjectStore, m_indexName); + if (!idbIndex) { + m_requestCallback->sendFailure("Could not get index"); + return; + } + + idbRequest = idbIndex->openCursor(scriptState(), m_idbKeyRange.get(), blink::WebIDBCursorDirectionNext); + } else { + idbRequest = idbObjectStore->openCursor(scriptState(), m_idbKeyRange.get(), blink::WebIDBCursorDirectionNext); + } + idbRequest->addEventListener(EventTypeNames::success, openCursorCallback, false); + } + + virtual RequestCallback* requestCallback() OVERRIDE { return m_requestCallback.get(); } + DataLoader(ScriptState* scriptState, PassRefPtr<RequestDataCallback> requestCallback, const String& objectStoreName, const String& indexName, IDBKeyRange* idbKeyRange, int skipCount, unsigned pageSize) + : ExecutableWithDatabase(scriptState) + , m_requestCallback(requestCallback) + , m_objectStoreName(objectStoreName) + , m_indexName(indexName) + , m_idbKeyRange(idbKeyRange) + , m_skipCount(skipCount) + , m_pageSize(pageSize) + { + } + + RefPtr<RequestDataCallback> m_requestCallback; + String m_objectStoreName; + String m_indexName; + Persistent<IDBKeyRange> m_idbKeyRange; + int m_skipCount; + unsigned m_pageSize; +}; + +LocalFrame* findFrameWithSecurityOrigin(Page* page, const String& securityOrigin) +{ + for (Frame* frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->isLocalFrame()) + continue; + RefPtr<SecurityOrigin> documentOrigin = toLocalFrame(frame)->document()->securityOrigin(); + if (documentOrigin->toRawString() == securityOrigin) + return toLocalFrame(frame); + } + return 0; +} + +} // namespace + +void InspectorIndexedDBAgent::provideTo(Page* page) +{ + OwnPtr<InspectorIndexedDBAgent> agent(adoptPtr(new InspectorIndexedDBAgent(page))); + page->inspectorController().registerModuleAgent(agent.release()); +} + +InspectorIndexedDBAgent::InspectorIndexedDBAgent(Page* page) + : InspectorBaseAgent<InspectorIndexedDBAgent>("IndexedDB") + , m_page(page) +{ +} + +InspectorIndexedDBAgent::~InspectorIndexedDBAgent() +{ +} + +void InspectorIndexedDBAgent::clearFrontend() +{ + disable(0); +} + +void InspectorIndexedDBAgent::restore() +{ + if (m_state->getBoolean(IndexedDBAgentState::indexedDBAgentEnabled)) { + ErrorString error; + enable(&error); + } +} + +void InspectorIndexedDBAgent::enable(ErrorString*) +{ + m_state->setBoolean(IndexedDBAgentState::indexedDBAgentEnabled, true); +} + +void InspectorIndexedDBAgent::disable(ErrorString*) +{ + m_state->setBoolean(IndexedDBAgentState::indexedDBAgentEnabled, false); +} + +static Document* assertDocument(ErrorString* errorString, LocalFrame* frame) +{ + Document* document = frame ? frame->document() : 0; + + if (!document) + *errorString = "No document for given frame found"; + + return document; +} + +static IDBFactory* assertIDBFactory(ErrorString* errorString, Document* document) +{ + LocalDOMWindow* domWindow = document->domWindow(); + if (!domWindow) { + *errorString = "No IndexedDB factory for given frame found"; + return 0; + } + IDBFactory* idbFactory = DOMWindowIndexedDatabase::indexedDB(*domWindow); + + if (!idbFactory) + *errorString = "No IndexedDB factory for given frame found"; + + return idbFactory; +} + +void InspectorIndexedDBAgent::requestDatabaseNames(ErrorString* errorString, const String& securityOrigin, PassRefPtr<RequestDatabaseNamesCallback> requestCallback) +{ + LocalFrame* frame = findFrameWithSecurityOrigin(m_page, securityOrigin); + Document* document = assertDocument(errorString, frame); + if (!document) + return; + IDBFactory* idbFactory = assertIDBFactory(errorString, document); + if (!idbFactory) + return; + + ScriptState* scriptState = ScriptState::forMainWorld(frame); + ScriptState::Scope scope(scriptState); + TrackExceptionState exceptionState; + IDBRequest* idbRequest = idbFactory->getDatabaseNames(scriptState, exceptionState); + if (exceptionState.hadException()) { + requestCallback->sendFailure("Could not obtain database names."); + return; + } + idbRequest->addEventListener(EventTypeNames::success, GetDatabaseNamesCallback::create(requestCallback, document->securityOrigin()->toRawString()), false); +} + +void InspectorIndexedDBAgent::requestDatabase(ErrorString* errorString, const String& securityOrigin, const String& databaseName, PassRefPtr<RequestDatabaseCallback> requestCallback) +{ + LocalFrame* frame = findFrameWithSecurityOrigin(m_page, securityOrigin); + Document* document = assertDocument(errorString, frame); + if (!document) + return; + IDBFactory* idbFactory = assertIDBFactory(errorString, document); + if (!idbFactory) + return; + + ScriptState* scriptState = ScriptState::forMainWorld(frame); + ScriptState::Scope scope(scriptState); + RefPtr<DatabaseLoader> databaseLoader = DatabaseLoader::create(scriptState, requestCallback); + databaseLoader->start(idbFactory, document->securityOrigin(), databaseName); +} + +void InspectorIndexedDBAgent::requestData(ErrorString* errorString, const String& securityOrigin, const String& databaseName, const String& objectStoreName, const String& indexName, int skipCount, int pageSize, const RefPtr<JSONObject>* keyRange, PassRefPtr<RequestDataCallback> requestCallback) +{ + LocalFrame* frame = findFrameWithSecurityOrigin(m_page, securityOrigin); + Document* document = assertDocument(errorString, frame); + if (!document) + return; + IDBFactory* idbFactory = assertIDBFactory(errorString, document); + if (!idbFactory) + return; + + IDBKeyRange* idbKeyRange = keyRange ? idbKeyRangeFromKeyRange(keyRange->get()) : 0; + if (keyRange && !idbKeyRange) { + *errorString = "Can not parse key range."; + return; + } + + ScriptState* scriptState = ScriptState::forMainWorld(frame); + ScriptState::Scope scope(scriptState); + RefPtr<DataLoader> dataLoader = DataLoader::create(scriptState, requestCallback, objectStoreName, indexName, idbKeyRange, skipCount, pageSize); + dataLoader->start(idbFactory, document->securityOrigin(), databaseName); +} + +class ClearObjectStoreListener FINAL : public EventListener { + WTF_MAKE_NONCOPYABLE(ClearObjectStoreListener); +public: + static PassRefPtr<ClearObjectStoreListener> create(PassRefPtr<ClearObjectStoreCallback> requestCallback) + { + return adoptRef(new ClearObjectStoreListener(requestCallback)); + } + + virtual ~ClearObjectStoreListener() { } + + virtual bool operator==(const EventListener& other) OVERRIDE + { + return this == &other; + } + + virtual void handleEvent(ExecutionContext*, Event* event) OVERRIDE + { + if (!m_requestCallback->isActive()) + return; + if (event->type() != EventTypeNames::complete) { + m_requestCallback->sendFailure("Unexpected event type."); + return; + } + + m_requestCallback->sendSuccess(); + } +private: + ClearObjectStoreListener(PassRefPtr<ClearObjectStoreCallback> requestCallback) + : EventListener(EventListener::CPPEventListenerType) + , m_requestCallback(requestCallback) + { + } + + RefPtr<ClearObjectStoreCallback> m_requestCallback; +}; + + +class ClearObjectStore FINAL : public ExecutableWithDatabase { +public: + static PassRefPtr<ClearObjectStore> create(ScriptState* scriptState, const String& objectStoreName, PassRefPtr<ClearObjectStoreCallback> requestCallback) + { + return adoptRef(new ClearObjectStore(scriptState, objectStoreName, requestCallback)); + } + + ClearObjectStore(ScriptState* scriptState, const String& objectStoreName, PassRefPtr<ClearObjectStoreCallback> requestCallback) + : ExecutableWithDatabase(scriptState) + , m_objectStoreName(objectStoreName) + , m_requestCallback(requestCallback) + { + } + + virtual void execute(IDBDatabase* idbDatabase) OVERRIDE + { + if (!requestCallback()->isActive()) + return; + IDBTransaction* idbTransaction = transactionForDatabase(context(), idbDatabase, m_objectStoreName, IDBTransaction::modeReadWrite()); + if (!idbTransaction) { + m_requestCallback->sendFailure("Could not get transaction"); + return; + } + IDBObjectStore* idbObjectStore = objectStoreForTransaction(idbTransaction, m_objectStoreName); + if (!idbObjectStore) { + m_requestCallback->sendFailure("Could not get object store"); + return; + } + + TrackExceptionState exceptionState; + idbObjectStore->clear(scriptState(), exceptionState); + ASSERT(!exceptionState.hadException()); + if (exceptionState.hadException()) { + ExceptionCode ec = exceptionState.code(); + m_requestCallback->sendFailure(String::format("Could not clear object store '%s': %d", m_objectStoreName.utf8().data(), ec)); + return; + } + idbTransaction->addEventListener(EventTypeNames::complete, ClearObjectStoreListener::create(m_requestCallback), false); + } + + virtual RequestCallback* requestCallback() OVERRIDE { return m_requestCallback.get(); } +private: + const String m_objectStoreName; + RefPtr<ClearObjectStoreCallback> m_requestCallback; +}; + +void InspectorIndexedDBAgent::clearObjectStore(ErrorString* errorString, const String& securityOrigin, const String& databaseName, const String& objectStoreName, PassRefPtr<ClearObjectStoreCallback> requestCallback) +{ + LocalFrame* frame = findFrameWithSecurityOrigin(m_page, securityOrigin); + Document* document = assertDocument(errorString, frame); + if (!document) + return; + IDBFactory* idbFactory = assertIDBFactory(errorString, document); + if (!idbFactory) + return; + + ScriptState* scriptState = ScriptState::forMainWorld(frame); + ScriptState::Scope scope(scriptState); + RefPtr<ClearObjectStore> clearObjectStore = ClearObjectStore::create(scriptState, objectStoreName, requestCallback); + clearObjectStore->start(idbFactory, document->securityOrigin(), databaseName); +} + +} // namespace WebCore |