/* * 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" #if ENABLE(INSPECTOR) && ENABLE(INDEXED_DATABASE) #include "InspectorIndexedDBAgent.h" #include "DOMStringList.h" #include "DOMWindow.h" #include "DOMWindowIndexedDatabase.h" #include "Document.h" #include "Event.h" #include "EventListener.h" #include "EventTarget.h" #include "ExceptionCode.h" #include "Frame.h" #include "IDBCursor.h" #include "IDBCursorWithValue.h" #include "IDBDatabase.h" #include "IDBDatabaseCallbacks.h" #include "IDBDatabaseMetadata.h" #include "IDBFactory.h" #include "IDBIndex.h" #include "IDBKey.h" #include "IDBKeyPath.h" #include "IDBKeyRange.h" #include "IDBObjectStore.h" #include "IDBOpenDBRequest.h" #include "IDBPendingTransactionMonitor.h" #include "IDBRequest.h" #include "IDBTransaction.h" #include "InspectorPageAgent.h" #include "InspectorWebFrontendDispatchers.h" #include "InstrumentingAgents.h" #include "SecurityOrigin.h" #include #include #include #include using Inspector::TypeBuilder::Array; using Inspector::TypeBuilder::IndexedDB::DatabaseWithObjectStores; using Inspector::TypeBuilder::IndexedDB::DataEntry; using Inspector::TypeBuilder::IndexedDB::Key; using Inspector::TypeBuilder::IndexedDB::KeyPath; using Inspector::TypeBuilder::IndexedDB::KeyRange; using Inspector::TypeBuilder::IndexedDB::ObjectStore; using Inspector::TypeBuilder::IndexedDB::ObjectStoreIndex; typedef Inspector::InspectorBackendDispatcher::CallbackBase RequestCallback; typedef Inspector::InspectorIndexedDBBackendDispatcherHandler::RequestDatabaseNamesCallback RequestDatabaseNamesCallback; typedef Inspector::InspectorIndexedDBBackendDispatcherHandler::RequestDatabaseCallback RequestDatabaseCallback; typedef Inspector::InspectorIndexedDBBackendDispatcherHandler::RequestDataCallback RequestDataCallback; typedef Inspector::InspectorIndexedDBBackendDispatcherHandler::ClearObjectStoreCallback ClearObjectStoreCallback; using namespace Inspector; namespace WebCore { namespace { class GetDatabaseNamesCallback : public EventListener { WTF_MAKE_NONCOPYABLE(GetDatabaseNamesCallback); public: static PassRefPtr create(PassRefPtr 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(ScriptExecutionContext*, Event* event) override { if (!m_requestCallback->isActive()) return; if (event->type() != eventNames().successEvent) { m_requestCallback->sendFailure("Unexpected event type."); return; } IDBRequest* idbRequest = static_cast(event->target()); ExceptionCode ec = 0; RefPtr requestResult = idbRequest->result(ec); if (ec) { m_requestCallback->sendFailure("Could not get result in callback."); return; } if (requestResult->type() != IDBAny::DOMStringListType) { m_requestCallback->sendFailure("Unexpected result type."); return; } RefPtr databaseNamesList = requestResult->domStringList(); RefPtr> databaseNames = Inspector::TypeBuilder::Array::create(); for (size_t i = 0; i < databaseNamesList->length(); ++i) databaseNames->addItem(databaseNamesList->item(i)); m_requestCallback->sendSuccess(databaseNames.release()); } private: GetDatabaseNamesCallback(PassRefPtr requestCallback, const String& securityOrigin) : EventListener(EventListener::CPPEventListenerType) , m_requestCallback(requestCallback) , m_securityOrigin(securityOrigin) { } RefPtr m_requestCallback; String m_securityOrigin; }; class ExecutableWithDatabase : public RefCounted { public: ExecutableWithDatabase(ScriptExecutionContext* context) : m_context(context) { } virtual ~ExecutableWithDatabase() { }; void start(IDBFactory*, SecurityOrigin*, const String& databaseName); virtual void execute(PassRefPtr) = 0; virtual RequestCallback* requestCallback() = 0; ScriptExecutionContext* context() { return m_context; }; private: ScriptExecutionContext* m_context; }; class OpenDatabaseCallback : public EventListener { public: static PassRefPtr create(ExecutableWithDatabase* executableWithDatabase) { return adoptRef(new OpenDatabaseCallback(executableWithDatabase)); } virtual ~OpenDatabaseCallback() { } virtual bool operator==(const EventListener& other) override { return this == &other; } virtual void handleEvent(ScriptExecutionContext*, Event* event) override { if (event->type() != eventNames().successEvent) { m_executableWithDatabase->requestCallback()->sendFailure("Unexpected event type."); return; } IDBOpenDBRequest* idbOpenDBRequest = static_cast(event->target()); ExceptionCode ec = 0; RefPtr requestResult = idbOpenDBRequest->result(ec); if (ec) { m_executableWithDatabase->requestCallback()->sendFailure("Could not get result in callback."); return; } if (requestResult->type() != IDBAny::IDBDatabaseType) { m_executableWithDatabase->requestCallback()->sendFailure("Unexpected result type."); return; } RefPtr idbDatabase = requestResult->idbDatabase(); m_executableWithDatabase->execute(idbDatabase); IDBPendingTransactionMonitor::deactivateNewTransactions(); idbDatabase->close(); } private: OpenDatabaseCallback(ExecutableWithDatabase* executableWithDatabase) : EventListener(EventListener::CPPEventListenerType) , m_executableWithDatabase(executableWithDatabase) { } RefPtr m_executableWithDatabase; }; void ExecutableWithDatabase::start(IDBFactory* idbFactory, SecurityOrigin*, const String& databaseName) { RefPtr callback = OpenDatabaseCallback::create(this); ExceptionCode ec = 0; RefPtr idbOpenDBRequest = idbFactory->open(context(), databaseName, ec); if (ec) { requestCallback()->sendFailure("Could not open database."); return; } idbOpenDBRequest->addEventListener(eventNames().successEvent, callback, false); } static PassRefPtr transactionForDatabase(ScriptExecutionContext* scriptExecutionContext, IDBDatabase* idbDatabase, const String& objectStoreName, const String& mode = IDBTransaction::modeReadOnly()) { ExceptionCode ec = 0; RefPtr idbTransaction = idbDatabase->transaction(scriptExecutionContext, objectStoreName, mode, ec); if (ec) return nullptr; return idbTransaction; } static PassRefPtr objectStoreForTransaction(IDBTransaction* idbTransaction, const String& objectStoreName) { ExceptionCode ec = 0; RefPtr idbObjectStore = idbTransaction->objectStore(objectStoreName, ec); if (ec) return nullptr; return idbObjectStore; } static PassRefPtr indexForObjectStore(IDBObjectStore* idbObjectStore, const String& indexName) { ExceptionCode ec = 0; RefPtr idbIndex = idbObjectStore->index(indexName, ec); if (ec) return nullptr; return idbIndex; } static PassRefPtr keyPathFromIDBKeyPath(const IDBKeyPath& idbKeyPath) { RefPtr 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> array = Inspector::TypeBuilder::Array::create(); const Vector& 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 : public ExecutableWithDatabase { public: static PassRefPtr create(ScriptExecutionContext* context, PassRefPtr requestCallback) { return adoptRef(new DatabaseLoader(context, requestCallback)); } virtual ~DatabaseLoader() { } virtual void execute(PassRefPtr prpDatabase) override { RefPtr idbDatabase = prpDatabase; if (!requestCallback()->isActive()) return; const IDBDatabaseMetadata databaseMetadata = idbDatabase->metadata(); RefPtr> objectStores = Inspector::TypeBuilder::Array::create(); for (const IDBObjectStoreMetadata& objectStoreMetadata : databaseMetadata.objectStores.values()) { RefPtr> indexes = Inspector::TypeBuilder::Array::create(); for (const IDBIndexMetadata& indexMetadata : objectStoreMetadata.indexes.values()) { RefPtr objectStoreIndex = ObjectStoreIndex::create() .setName(indexMetadata.name) .setKeyPath(keyPathFromIDBKeyPath(indexMetadata.keyPath)) .setUnique(indexMetadata.unique) .setMultiEntry(indexMetadata.multiEntry); indexes->addItem(objectStoreIndex); } RefPtr objectStore = ObjectStore::create() .setName(objectStoreMetadata.name) .setKeyPath(keyPathFromIDBKeyPath(objectStoreMetadata.keyPath)) .setAutoIncrement(objectStoreMetadata.autoIncrement) .setIndexes(indexes); objectStores->addItem(objectStore); } RefPtr result = DatabaseWithObjectStores::create() .setName(databaseMetadata.name) .setVersion(databaseMetadata.version) .setObjectStores(objectStores); m_requestCallback->sendSuccess(result); } virtual RequestCallback* requestCallback() override { return m_requestCallback.get(); } private: DatabaseLoader(ScriptExecutionContext* context, PassRefPtr requestCallback) : ExecutableWithDatabase(context) , m_requestCallback(requestCallback) { } RefPtr m_requestCallback; }; static PassRefPtr idbKeyFromInspectorObject(InspectorObject* key) { RefPtr idbKey; String type; if (!key->getString("type", &type)) return nullptr; DEPRECATED_DEFINE_STATIC_LOCAL(String, number, (ASCIILiteral("number"))); DEPRECATED_DEFINE_STATIC_LOCAL(String, string, (ASCIILiteral("string"))); DEPRECATED_DEFINE_STATIC_LOCAL(String, date, (ASCIILiteral("date"))); DEPRECATED_DEFINE_STATIC_LOCAL(String, array, (ASCIILiteral("array"))); if (type == number) { double number; if (!key->getNumber("number", &number)) return nullptr; idbKey = IDBKey::createNumber(number); } else if (type == string) { String string; if (!key->getString("string", &string)) return nullptr; idbKey = IDBKey::createString(string); } else if (type == date) { double date; if (!key->getNumber("date", &date)) return nullptr; idbKey = IDBKey::createDate(date); } else if (type == array) { IDBKey::KeyArray keyArray; RefPtr array = key->getArray("array"); for (size_t i = 0; i < array->length(); ++i) { RefPtr value = array->get(i); RefPtr object; if (!value->asObject(&object)) return nullptr; keyArray.append(idbKeyFromInspectorObject(object.get())); } idbKey = IDBKey::createArray(keyArray); } else return nullptr; return idbKey.release(); } static PassRefPtr idbKeyRangeFromKeyRange(InspectorObject* keyRange) { RefPtr lower = keyRange->getObject("lower"); RefPtr idbLower = lower ? idbKeyFromInspectorObject(lower.get()) : nullptr; if (lower && !idbLower) return nullptr; RefPtr upper = keyRange->getObject("upper"); RefPtr idbUpper = upper ? idbKeyFromInspectorObject(upper.get()) : nullptr; if (upper && !idbUpper) return nullptr; bool lowerOpen; if (!keyRange->getBoolean("lowerOpen", &lowerOpen)) return nullptr; IDBKeyRange::LowerBoundType lowerBoundType = lowerOpen ? IDBKeyRange::LowerBoundOpen : IDBKeyRange::LowerBoundClosed; bool upperOpen; if (!keyRange->getBoolean("upperOpen", &upperOpen)) return nullptr; IDBKeyRange::UpperBoundType upperBoundType = upperOpen ? IDBKeyRange::UpperBoundOpen : IDBKeyRange::UpperBoundClosed; RefPtr idbKeyRange = IDBKeyRange::create(idbLower, idbUpper, lowerBoundType, upperBoundType); return idbKeyRange.release(); } class DataLoader; class OpenCursorCallback : public EventListener { public: static PassRefPtr create(InjectedScript injectedScript, PassRefPtr requestCallback, int skipCount, unsigned pageSize) { return adoptRef(new OpenCursorCallback(injectedScript, requestCallback, skipCount, pageSize)); } virtual ~OpenCursorCallback() { } virtual bool operator==(const EventListener& other) override { return this == &other; } virtual void handleEvent(ScriptExecutionContext*, Event* event) override { if (event->type() != eventNames().successEvent) { m_requestCallback->sendFailure("Unexpected event type."); return; } IDBRequest* idbRequest = static_cast(event->target()); ExceptionCode ec = 0; RefPtr requestResult = idbRequest->result(ec); if (ec) { m_requestCallback->sendFailure("Could not get result in callback."); return; } if (requestResult->type() == IDBAny::ScriptValueType) { end(false); return; } if (requestResult->type() != IDBAny::IDBCursorWithValueType) { m_requestCallback->sendFailure("Unexpected result type."); return; } RefPtr idbCursor = requestResult->idbCursorWithValue(); if (m_skipCount) { ExceptionCode ec = 0; idbCursor->advance(m_skipCount, ec); if (ec) 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. idbCursor->continueFunction(nullptr, ec); if (ec) { m_requestCallback->sendFailure("Could not continue cursor."); return; } RefPtr dataEntry = DataEntry::create() .setKey(m_injectedScript.wrapObject(idbCursor->key(), String())) .setPrimaryKey(m_injectedScript.wrapObject(idbCursor->primaryKey(), String())) .setValue(m_injectedScript.wrapObject(idbCursor->value(), String())); m_result->addItem(dataEntry); } void end(bool hasMore) { if (!m_requestCallback->isActive()) return; m_requestCallback->sendSuccess(m_result.release(), hasMore); } private: OpenCursorCallback(InjectedScript injectedScript, PassRefPtr requestCallback, int skipCount, unsigned pageSize) : EventListener(EventListener::CPPEventListenerType) , m_injectedScript(injectedScript) , m_requestCallback(requestCallback) , m_skipCount(skipCount) , m_pageSize(pageSize) { m_result = Array::create(); } InjectedScript m_injectedScript; RefPtr m_requestCallback; int m_skipCount; unsigned m_pageSize; RefPtr> m_result; }; class DataLoader : public ExecutableWithDatabase { public: static PassRefPtr create(ScriptExecutionContext* context, PassRefPtr requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, PassRefPtr idbKeyRange, int skipCount, unsigned pageSize) { return adoptRef(new DataLoader(context, requestCallback, injectedScript, objectStoreName, indexName, idbKeyRange, skipCount, pageSize)); } virtual ~DataLoader() { } virtual void execute(PassRefPtr prpDatabase) override { RefPtr idbDatabase = prpDatabase; if (!requestCallback()->isActive()) return; RefPtr idbTransaction = transactionForDatabase(context(), idbDatabase.get(), m_objectStoreName); if (!idbTransaction) { m_requestCallback->sendFailure("Could not get transaction"); return; } RefPtr idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName); if (!idbObjectStore) { m_requestCallback->sendFailure("Could not get object store"); return; } RefPtr openCursorCallback = OpenCursorCallback::create(m_injectedScript, m_requestCallback, m_skipCount, m_pageSize); ExceptionCode ec = 0; RefPtr idbRequest; if (!m_indexName.isEmpty()) { RefPtr idbIndex = indexForObjectStore(idbObjectStore.get(), m_indexName); if (!idbIndex) { m_requestCallback->sendFailure("Could not get index"); return; } idbRequest = idbIndex->openCursor(context(), PassRefPtr(m_idbKeyRange), ec); } else idbRequest = idbObjectStore->openCursor(context(), PassRefPtr(m_idbKeyRange), ec); idbRequest->addEventListener(eventNames().successEvent, openCursorCallback, false); } virtual RequestCallback* requestCallback() override { return m_requestCallback.get(); } DataLoader(ScriptExecutionContext* scriptExecutionContext, PassRefPtr requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, PassRefPtr idbKeyRange, int skipCount, unsigned pageSize) : ExecutableWithDatabase(scriptExecutionContext) , m_requestCallback(requestCallback) , m_injectedScript(injectedScript) , m_objectStoreName(objectStoreName) , m_indexName(indexName) , m_idbKeyRange(idbKeyRange) , m_skipCount(skipCount) , m_pageSize(pageSize) { } RefPtr m_requestCallback; InjectedScript m_injectedScript; String m_objectStoreName; String m_indexName; RefPtr m_idbKeyRange; int m_skipCount; unsigned m_pageSize; }; } // namespace InspectorIndexedDBAgent::InspectorIndexedDBAgent(InstrumentingAgents* instrumentingAgents, InjectedScriptManager* injectedScriptManager, InspectorPageAgent* pageAgent) : InspectorAgentBase(ASCIILiteral("IndexedDB"), instrumentingAgents) , m_injectedScriptManager(injectedScriptManager) , m_pageAgent(pageAgent) { } InspectorIndexedDBAgent::~InspectorIndexedDBAgent() { } void InspectorIndexedDBAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel*, InspectorBackendDispatcher* backendDispatcher) { m_backendDispatcher = InspectorIndexedDBBackendDispatcher::create(backendDispatcher, this); } void InspectorIndexedDBAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) { m_backendDispatcher.clear(); disable(nullptr); } void InspectorIndexedDBAgent::enable(ErrorString*) { } void InspectorIndexedDBAgent::disable(ErrorString*) { } static Document* assertDocument(ErrorString* errorString, Frame* frame) { Document* document = frame ? frame->document() : nullptr; if (!document) *errorString = "No document for given frame found"; return document; } static IDBFactory* assertIDBFactory(ErrorString* errorString, Document* document) { DOMWindow* domWindow = document->domWindow(); if (!domWindow) { *errorString = "No IndexedDB factory for given frame found"; return nullptr; } 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 requestCallback) { Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin); Document* document = assertDocument(errorString, frame); if (!document) return; IDBFactory* idbFactory = assertIDBFactory(errorString, document); if (!idbFactory) return; ExceptionCode ec = 0; RefPtr idbRequest = idbFactory->getDatabaseNames(document, ec); if (!idbRequest || ec) { requestCallback->sendFailure("Could not obtain database names."); return; } idbRequest->addEventListener(eventNames().successEvent, GetDatabaseNamesCallback::create(requestCallback, document->securityOrigin()->toRawString()), false); } void InspectorIndexedDBAgent::requestDatabase(ErrorString* errorString, const String& securityOrigin, const String& databaseName, PassRefPtr requestCallback) { Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin); Document* document = assertDocument(errorString, frame); if (!document) return; IDBFactory* idbFactory = assertIDBFactory(errorString, document); if (!idbFactory) return; RefPtr databaseLoader = DatabaseLoader::create(document, 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* keyRange, PassRefPtr requestCallback) { Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin); Document* document = assertDocument(errorString, frame); if (!document) return; IDBFactory* idbFactory = assertIDBFactory(errorString, document); if (!idbFactory) return; InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldExecState(frame)); RefPtr idbKeyRange = keyRange ? idbKeyRangeFromKeyRange(keyRange->get()) : nullptr; if (keyRange && !idbKeyRange) { *errorString = "Can not parse key range."; return; } RefPtr dataLoader = DataLoader::create(document, requestCallback, injectedScript, objectStoreName, indexName, idbKeyRange, skipCount, pageSize); dataLoader->start(idbFactory, document->securityOrigin(), databaseName); } class ClearObjectStoreListener : public EventListener { WTF_MAKE_NONCOPYABLE(ClearObjectStoreListener); public: static PassRefPtr create(PassRefPtr requestCallback) { return adoptRef(new ClearObjectStoreListener(requestCallback)); } virtual ~ClearObjectStoreListener() { } virtual bool operator==(const EventListener& other) override { return this == &other; } virtual void handleEvent(ScriptExecutionContext*, Event* event) override { if (!m_requestCallback->isActive()) return; if (event->type() != eventNames().completeEvent) { m_requestCallback->sendFailure("Unexpected event type."); return; } m_requestCallback->sendSuccess(); } private: ClearObjectStoreListener(PassRefPtr requestCallback) : EventListener(EventListener::CPPEventListenerType) , m_requestCallback(requestCallback) { } RefPtr m_requestCallback; }; class ClearObjectStore : public ExecutableWithDatabase { public: static PassRefPtr create(ScriptExecutionContext* context, const String& objectStoreName, PassRefPtr requestCallback) { return adoptRef(new ClearObjectStore(context, objectStoreName, requestCallback)); } ClearObjectStore(ScriptExecutionContext* context, const String& objectStoreName, PassRefPtr requestCallback) : ExecutableWithDatabase(context) , m_objectStoreName(objectStoreName) , m_requestCallback(requestCallback) { } virtual void execute(PassRefPtr prpDatabase) override { RefPtr idbDatabase = prpDatabase; if (!requestCallback()->isActive()) return; RefPtr idbTransaction = transactionForDatabase(context(), idbDatabase.get(), m_objectStoreName, IDBTransaction::modeReadWrite()); if (!idbTransaction) { m_requestCallback->sendFailure("Could not get transaction"); return; } RefPtr idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName); if (!idbObjectStore) { m_requestCallback->sendFailure("Could not get object store"); return; } ExceptionCode ec = 0; RefPtr idbRequest = idbObjectStore->clear(context(), ec); ASSERT(!ec); if (ec) { m_requestCallback->sendFailure(String::format("Could not clear object store '%s': %d", m_objectStoreName.utf8().data(), ec)); return; } idbTransaction->addEventListener(eventNames().completeEvent, ClearObjectStoreListener::create(m_requestCallback), false); } virtual RequestCallback* requestCallback() override { return m_requestCallback.get(); } private: const String m_objectStoreName; RefPtr m_requestCallback; }; void InspectorIndexedDBAgent::clearObjectStore(ErrorString* errorString, const String& securityOrigin, const String& databaseName, const String& objectStoreName, PassRefPtr requestCallback) { Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin); Document* document = assertDocument(errorString, frame); if (!document) return; IDBFactory* idbFactory = assertIDBFactory(errorString, document); if (!idbFactory) return; RefPtr clearObjectStore = ClearObjectStore::create(document, objectStoreName, requestCallback); clearObjectStore->start(idbFactory, document->securityOrigin(), databaseName); } } // namespace WebCore #endif // ENABLE(INSPECTOR) && ENABLE(INDEXED_DATABASE)