blob: 8199b53cb669e2a5ebb5112f9714f195bab01a25 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2015 Apple 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(INDEXED_DATABASE)
#include "InspectorIndexedDBAgent.h"
#include "DOMStringList.h"
#include "DOMWindow.h"
#include "DOMWindowIndexedDatabase.h"
#include "Document.h"
#include "Event.h"
#include "EventListener.h"
#include "EventNames.h"
#include "EventTarget.h"
#include "Frame.h"
#include "IDBBindingUtilities.h"
#include "IDBCursor.h"
#include "IDBCursorWithValue.h"
#include "IDBDatabase.h"
#include "IDBFactory.h"
#include "IDBIndex.h"
#include "IDBKey.h"
#include "IDBKeyPath.h"
#include "IDBKeyRange.h"
#include "IDBObjectStore.h"
#include "IDBOpenDBRequest.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
#include "InspectorPageAgent.h"
#include "InstrumentingAgents.h"
#include "ScriptState.h"
#include "SecurityOrigin.h"
#include <inspector/InjectedScript.h>
#include <inspector/InjectedScriptManager.h>
#include <inspector/InspectorFrontendDispatchers.h>
#include <inspector/InspectorFrontendRouter.h>
#include <inspector/InspectorValues.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/Vector.h>
using Inspector::Protocol::Array;
using Inspector::Protocol::IndexedDB::DatabaseWithObjectStores;
using Inspector::Protocol::IndexedDB::DataEntry;
using Inspector::Protocol::IndexedDB::Key;
using Inspector::Protocol::IndexedDB::KeyPath;
using Inspector::Protocol::IndexedDB::KeyRange;
using Inspector::Protocol::IndexedDB::ObjectStore;
using Inspector::Protocol::IndexedDB::ObjectStoreIndex;
typedef Inspector::BackendDispatcher::CallbackBase RequestCallback;
typedef Inspector::IndexedDBBackendDispatcherHandler::RequestDatabaseNamesCallback RequestDatabaseNamesCallback;
typedef Inspector::IndexedDBBackendDispatcherHandler::RequestDatabaseCallback RequestDatabaseCallback;
typedef Inspector::IndexedDBBackendDispatcherHandler::RequestDataCallback RequestDataCallback;
typedef Inspector::IndexedDBBackendDispatcherHandler::ClearObjectStoreCallback ClearObjectStoreCallback;
using namespace Inspector;
namespace WebCore {
namespace {
class ExecutableWithDatabase : public RefCounted<ExecutableWithDatabase> {
public:
ExecutableWithDatabase(ScriptExecutionContext* context)
: m_context(context) { }
virtual ~ExecutableWithDatabase() { }
void start(IDBFactory*, SecurityOrigin*, const String& databaseName);
virtual void execute(IDBDatabase&) = 0;
virtual RequestCallback& requestCallback() = 0;
ScriptExecutionContext* context() const { return m_context; }
private:
ScriptExecutionContext* m_context;
};
class OpenDatabaseCallback final : public EventListener {
public:
static Ref<OpenDatabaseCallback> create(ExecutableWithDatabase& executableWithDatabase)
{
return adoptRef(*new OpenDatabaseCallback(executableWithDatabase));
}
bool operator==(const EventListener& other) const final
{
return this == &other;
}
void handleEvent(ScriptExecutionContext*, Event* event) final
{
if (event->type() != eventNames().successEvent) {
m_executableWithDatabase->requestCallback().sendFailure("Unexpected event type.");
return;
}
auto& request = static_cast<IDBOpenDBRequest&>(*event->target());
if (!request.isDone()) {
m_executableWithDatabase->requestCallback().sendFailure("Could not get result in callback.");
return;
}
auto databaseResult = request.databaseResult();
if (!databaseResult) {
m_executableWithDatabase->requestCallback().sendFailure("Unexpected result type.");
return;
}
m_executableWithDatabase->execute(*databaseResult);
databaseResult->close();
}
private:
OpenDatabaseCallback(ExecutableWithDatabase& executableWithDatabase)
: EventListener(EventListener::CPPEventListenerType)
, m_executableWithDatabase(executableWithDatabase) { }
Ref<ExecutableWithDatabase> m_executableWithDatabase;
};
void ExecutableWithDatabase::start(IDBFactory* idbFactory, SecurityOrigin*, const String& databaseName)
{
if (!context()) {
requestCallback().sendFailure("Could not open database.");
return;
}
auto result = idbFactory->open(*context(), databaseName, Nullopt);
if (result.hasException()) {
requestCallback().sendFailure("Could not open database.");
return;
}
result.releaseReturnValue()->addEventListener(eventNames().successEvent, OpenDatabaseCallback::create(*this), false);
}
static RefPtr<KeyPath> keyPathFromIDBKeyPath(const Optional<IDBKeyPath>& idbKeyPath)
{
if (!idbKeyPath)
return KeyPath::create().setType(KeyPath::Type::Null).release();
auto visitor = WTF::makeVisitor([](const String& string) {
RefPtr<KeyPath> keyPath = KeyPath::create().setType(KeyPath::Type::String).release();
keyPath->setString(string);
return keyPath;
}, [](const Vector<String>& vector) {
auto array = Inspector::Protocol::Array<String>::create();
for (auto& string : vector)
array->addItem(string);
RefPtr<KeyPath> keyPath = KeyPath::create().setType(KeyPath::Type::Array).release();
keyPath->setArray(WTFMove(array));
return keyPath;
});
return WTF::visit(visitor, idbKeyPath.value());
}
static RefPtr<IDBTransaction> transactionForDatabase(IDBDatabase* idbDatabase, const String& objectStoreName, IDBTransactionMode mode = IDBTransactionMode::Readonly)
{
auto result = idbDatabase->transaction(objectStoreName, mode);
if (result.hasException())
return nullptr;
return result.releaseReturnValue();
}
static RefPtr<IDBObjectStore> objectStoreForTransaction(IDBTransaction* idbTransaction, const String& objectStoreName)
{
auto result = idbTransaction->objectStore(objectStoreName);
if (result.hasException())
return nullptr;
return result.releaseReturnValue();
}
static RefPtr<IDBIndex> indexForObjectStore(IDBObjectStore* idbObjectStore, const String& indexName)
{
auto index = idbObjectStore->index(indexName);
if (index.hasException())
return nullptr;
return index.releaseReturnValue();
}
class DatabaseLoader final : public ExecutableWithDatabase {
public:
static Ref<DatabaseLoader> create(ScriptExecutionContext* context, Ref<RequestDatabaseCallback>&& requestCallback)
{
return adoptRef(*new DatabaseLoader(context, WTFMove(requestCallback)));
}
virtual ~DatabaseLoader() { }
void execute(IDBDatabase& database) override
{
if (!requestCallback().isActive())
return;
auto& databaseInfo = database.info();
auto objectStores = Inspector::Protocol::Array<Inspector::Protocol::IndexedDB::ObjectStore>::create();
auto objectStoreNames = databaseInfo.objectStoreNames();
for (auto& name : objectStoreNames) {
auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(name);
if (!objectStoreInfo)
continue;
auto indexes = Inspector::Protocol::Array<Inspector::Protocol::IndexedDB::ObjectStoreIndex>::create();
for (auto& indexInfo : objectStoreInfo->indexMap().values()) {
auto objectStoreIndex = ObjectStoreIndex::create()
.setName(indexInfo.name())
.setKeyPath(keyPathFromIDBKeyPath(indexInfo.keyPath()))
.setUnique(indexInfo.unique())
.setMultiEntry(indexInfo.multiEntry())
.release();
indexes->addItem(WTFMove(objectStoreIndex));
}
auto objectStore = ObjectStore::create()
.setName(objectStoreInfo->name())
.setKeyPath(keyPathFromIDBKeyPath(objectStoreInfo->keyPath()))
.setAutoIncrement(objectStoreInfo->autoIncrement())
.setIndexes(WTFMove(indexes))
.release();
objectStores->addItem(WTFMove(objectStore));
}
auto result = DatabaseWithObjectStores::create()
.setName(databaseInfo.name())
.setVersion(databaseInfo.version())
.setObjectStores(WTFMove(objectStores))
.release();
m_requestCallback->sendSuccess(WTFMove(result));
}
RequestCallback& requestCallback() override { return m_requestCallback.get(); }
private:
DatabaseLoader(ScriptExecutionContext* context, Ref<RequestDatabaseCallback>&& requestCallback)
: ExecutableWithDatabase(context)
, m_requestCallback(WTFMove(requestCallback)) { }
Ref<RequestDatabaseCallback> m_requestCallback;
};
static RefPtr<IDBKey> idbKeyFromInspectorObject(InspectorObject* key)
{
String type;
if (!key->getString("type", type))
return nullptr;
static NeverDestroyed<const String> numberType(ASCIILiteral("number"));
static NeverDestroyed<const String> stringType(ASCIILiteral("string"));
static NeverDestroyed<const String> dateType(ASCIILiteral("date"));
static NeverDestroyed<const String> arrayType(ASCIILiteral("array"));
RefPtr<IDBKey> idbKey;
if (type == numberType) {
double number;
if (!key->getDouble("number", number))
return nullptr;
idbKey = IDBKey::createNumber(number);
} else if (type == stringType) {
String string;
if (!key->getString("string", string))
return nullptr;
idbKey = IDBKey::createString(string);
} else if (type == dateType) {
double date;
if (!key->getDouble("date", date))
return nullptr;
idbKey = IDBKey::createDate(date);
} else if (type == arrayType) {
Vector<RefPtr<IDBKey>> keyArray;
RefPtr<InspectorArray> array;
if (!key->getArray("array", array))
return nullptr;
for (size_t i = 0; i < array->length(); ++i) {
RefPtr<InspectorValue> value = array->get(i);
RefPtr<InspectorObject> object;
if (!value->asObject(object))
return nullptr;
keyArray.append(idbKeyFromInspectorObject(object.get()));
}
idbKey = IDBKey::createArray(keyArray);
} else
return nullptr;
return idbKey;
}
static RefPtr<IDBKeyRange> idbKeyRangeFromKeyRange(const InspectorObject* keyRange)
{
RefPtr<IDBKey> idbLower;
RefPtr<InspectorObject> lower;
if (keyRange->getObject(ASCIILiteral("lower"), lower)) {
idbLower = idbKeyFromInspectorObject(lower.get());
if (!idbLower)
return nullptr;
}
RefPtr<IDBKey> idbUpper;
RefPtr<InspectorObject> upper;
if (keyRange->getObject(ASCIILiteral("upper"), upper)) {
idbUpper = idbKeyFromInspectorObject(upper.get());
if (!idbUpper)
return nullptr;
}
bool lowerOpen;
if (!keyRange->getBoolean(ASCIILiteral("lowerOpen"), lowerOpen))
return nullptr;
bool upperOpen;
if (!keyRange->getBoolean(ASCIILiteral("upperOpen"), upperOpen))
return nullptr;
return IDBKeyRange::create(WTFMove(idbLower), WTFMove(idbUpper), lowerOpen, upperOpen);
}
class DataLoader;
class OpenCursorCallback : public EventListener {
public:
static Ref<OpenCursorCallback> create(InjectedScript injectedScript, Ref<RequestDataCallback>&& requestCallback, int skipCount, unsigned pageSize)
{
return adoptRef(*new OpenCursorCallback(injectedScript, WTFMove(requestCallback), skipCount, pageSize));
}
virtual ~OpenCursorCallback() { }
bool operator==(const EventListener& other) const override
{
return this == &other;
}
void handleEvent(ScriptExecutionContext* context, Event* event) override
{
if (event->type() != eventNames().successEvent) {
m_requestCallback->sendFailure("Unexpected event type.");
return;
}
auto& request = static_cast<IDBRequest&>(*event->target());
if (!request.isDone()) {
m_requestCallback->sendFailure("Could not get result in callback.");
return;
}
if (request.scriptResult()) {
end(false);
return;
}
auto* cursorResult = request.cursorResult();
if (!cursorResult) {
end(false);
return;
}
auto& cursor = *cursorResult;
if (m_skipCount) {
if (cursor.advance(m_skipCount).hasException())
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.
if (cursor.continueFunction(nullptr).hasException()) {
m_requestCallback->sendFailure("Could not continue cursor.");
return;
}
auto* state = context ? context->execState() : nullptr;
if (!state)
return;
auto dataEntry = DataEntry::create()
.setKey(m_injectedScript.wrapObject(cursor.key(), String(), true))
.setPrimaryKey(m_injectedScript.wrapObject(cursor.primaryKey(), String(), true))
.setValue(m_injectedScript.wrapObject(cursor.value(), String(), true))
.release();
m_result->addItem(WTFMove(dataEntry));
}
void end(bool hasMore)
{
if (!m_requestCallback->isActive())
return;
m_requestCallback->sendSuccess(WTFMove(m_result), hasMore);
}
private:
OpenCursorCallback(InjectedScript injectedScript, Ref<RequestDataCallback>&& requestCallback, int skipCount, unsigned pageSize)
: EventListener(EventListener::CPPEventListenerType)
, m_injectedScript(injectedScript)
, m_requestCallback(WTFMove(requestCallback))
, m_result(Array<DataEntry>::create())
, m_skipCount(skipCount)
, m_pageSize(pageSize)
{
}
InjectedScript m_injectedScript;
Ref<RequestDataCallback> m_requestCallback;
Ref<Array<DataEntry>> m_result;
int m_skipCount;
unsigned m_pageSize;
};
class DataLoader : public ExecutableWithDatabase {
public:
static Ref<DataLoader> create(ScriptExecutionContext* context, Ref<RequestDataCallback>&& requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, RefPtr<IDBKeyRange>&& idbKeyRange, int skipCount, unsigned pageSize)
{
return adoptRef(*new DataLoader(context, WTFMove(requestCallback), injectedScript, objectStoreName, indexName, WTFMove(idbKeyRange), skipCount, pageSize));
}
virtual ~DataLoader() { }
void execute(IDBDatabase& database) override
{
if (!requestCallback().isActive())
return;
auto idbTransaction = transactionForDatabase(&database, m_objectStoreName);
if (!idbTransaction) {
m_requestCallback->sendFailure("Could not get transaction");
return;
}
auto idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
if (!idbObjectStore) {
m_requestCallback->sendFailure("Could not get object store");
return;
}
TransactionActivator activator(idbTransaction.get());
RefPtr<IDBRequest> idbRequest;
auto* exec = context() ? context()->execState() : nullptr;
if (!m_indexName.isEmpty()) {
auto idbIndex = indexForObjectStore(idbObjectStore.get(), m_indexName);
if (!idbIndex) {
m_requestCallback->sendFailure("Could not get index");
return;
}
if (exec) {
auto result = idbIndex->openCursor(*exec, m_idbKeyRange.get(), IDBCursor::directionNext());
if (!result.hasException())
idbRequest = result.releaseReturnValue();
}
} else {
if (exec) {
auto result = idbObjectStore->openCursor(*exec, m_idbKeyRange.get(), IDBCursor::directionNext());
if (!result.hasException())
idbRequest = result.releaseReturnValue();
}
}
if (!idbRequest) {
m_requestCallback->sendFailure("Could not open cursor to populate database data");
return;
}
auto openCursorCallback = OpenCursorCallback::create(m_injectedScript, m_requestCallback.copyRef(), m_skipCount, m_pageSize);
idbRequest->addEventListener(eventNames().successEvent, WTFMove(openCursorCallback), false);
}
RequestCallback& requestCallback() override { return m_requestCallback.get(); }
DataLoader(ScriptExecutionContext* scriptExecutionContext, Ref<RequestDataCallback>&& requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, RefPtr<IDBKeyRange> idbKeyRange, int skipCount, unsigned pageSize)
: ExecutableWithDatabase(scriptExecutionContext)
, m_requestCallback(WTFMove(requestCallback))
, m_injectedScript(injectedScript)
, m_objectStoreName(objectStoreName)
, m_indexName(indexName)
, m_idbKeyRange(WTFMove(idbKeyRange))
, m_skipCount(skipCount)
, m_pageSize(pageSize) { }
Ref<RequestDataCallback> m_requestCallback;
InjectedScript m_injectedScript;
String m_objectStoreName;
String m_indexName;
RefPtr<IDBKeyRange> m_idbKeyRange;
int m_skipCount;
unsigned m_pageSize;
};
} // namespace
InspectorIndexedDBAgent::InspectorIndexedDBAgent(WebAgentContext& context, InspectorPageAgent* pageAgent)
: InspectorAgentBase(ASCIILiteral("IndexedDB"), context)
, m_injectedScriptManager(context.injectedScriptManager)
, m_backendDispatcher(Inspector::IndexedDBBackendDispatcher::create(context.backendDispatcher, this))
, m_pageAgent(pageAgent)
{
}
InspectorIndexedDBAgent::~InspectorIndexedDBAgent()
{
}
void InspectorIndexedDBAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
{
}
void InspectorIndexedDBAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
{
ErrorString unused;
disable(unused);
}
void InspectorIndexedDBAgent::enable(ErrorString&)
{
}
void InspectorIndexedDBAgent::disable(ErrorString&)
{
}
static Document* assertDocument(ErrorString& errorString, Frame* frame)
{
Document* document = frame ? frame->document() : nullptr;
if (!document)
errorString = ASCIILiteral("No document for given frame found");
return document;
}
static IDBFactory* assertIDBFactory(ErrorString& errorString, Document* document)
{
DOMWindow* domWindow = document->domWindow();
if (!domWindow) {
errorString = ASCIILiteral("No IndexedDB factory for given frame found");
return nullptr;
}
IDBFactory* idbFactory = DOMWindowIndexedDatabase::indexedDB(*domWindow);
if (!idbFactory)
errorString = ASCIILiteral("No IndexedDB factory for given frame found");
return idbFactory;
}
void InspectorIndexedDBAgent::requestDatabaseNames(ErrorString& errorString, const String& securityOrigin, Ref<RequestDatabaseNamesCallback>&& requestCallback)
{
Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
Document* document = assertDocument(errorString, frame);
if (!document)
return;
auto* openingOrigin = document->securityOrigin();
if (!openingOrigin)
return;
auto* topOrigin = document->topOrigin();
if (!topOrigin)
return;
IDBFactory* idbFactory = assertIDBFactory(errorString, document);
if (!idbFactory)
return;
RefPtr<RequestDatabaseNamesCallback> callback = WTFMove(requestCallback);
idbFactory->getAllDatabaseNames(*topOrigin, *openingOrigin, [callback](auto& databaseNames) {
if (!callback->isActive())
return;
Ref<Inspector::Protocol::Array<String>> databaseNameArray = Inspector::Protocol::Array<String>::create();
for (auto& databaseName : databaseNames)
databaseNameArray->addItem(databaseName);
callback->sendSuccess(WTFMove(databaseNameArray));
});
}
void InspectorIndexedDBAgent::requestDatabase(ErrorString& errorString, const String& securityOrigin, const String& databaseName, Ref<RequestDatabaseCallback>&& requestCallback)
{
Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
Document* document = assertDocument(errorString, frame);
if (!document)
return;
IDBFactory* idbFactory = assertIDBFactory(errorString, document);
if (!idbFactory)
return;
Ref<DatabaseLoader> databaseLoader = DatabaseLoader::create(document, WTFMove(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 InspectorObject* keyRange, Ref<RequestDataCallback>&& 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> idbKeyRange = keyRange ? idbKeyRangeFromKeyRange(keyRange) : nullptr;
if (keyRange && !idbKeyRange) {
errorString = ASCIILiteral("Can not parse key range.");
return;
}
Ref<DataLoader> dataLoader = DataLoader::create(document, WTFMove(requestCallback), injectedScript, objectStoreName, indexName, WTFMove(idbKeyRange), skipCount, pageSize);
dataLoader->start(idbFactory, document->securityOrigin(), databaseName);
}
class ClearObjectStoreListener : public EventListener {
WTF_MAKE_NONCOPYABLE(ClearObjectStoreListener);
public:
static Ref<ClearObjectStoreListener> create(Ref<ClearObjectStoreCallback> requestCallback)
{
return adoptRef(*new ClearObjectStoreListener(WTFMove(requestCallback)));
}
virtual ~ClearObjectStoreListener() { }
bool operator==(const EventListener& other) const override
{
return this == &other;
}
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(Ref<ClearObjectStoreCallback>&& requestCallback)
: EventListener(EventListener::CPPEventListenerType)
, m_requestCallback(WTFMove(requestCallback))
{
}
Ref<ClearObjectStoreCallback> m_requestCallback;
};
class ClearObjectStore : public ExecutableWithDatabase {
public:
static Ref<ClearObjectStore> create(ScriptExecutionContext* context, const String& objectStoreName, Ref<ClearObjectStoreCallback>&& requestCallback)
{
return adoptRef(*new ClearObjectStore(context, objectStoreName, WTFMove(requestCallback)));
}
ClearObjectStore(ScriptExecutionContext* context, const String& objectStoreName, Ref<ClearObjectStoreCallback>&& requestCallback)
: ExecutableWithDatabase(context)
, m_objectStoreName(objectStoreName)
, m_requestCallback(WTFMove(requestCallback))
{
}
void execute(IDBDatabase& database) override
{
if (!requestCallback().isActive())
return;
auto idbTransaction = transactionForDatabase(&database, m_objectStoreName, IDBTransactionMode::Readwrite);
if (!idbTransaction) {
m_requestCallback->sendFailure("Could not get transaction");
return;
}
auto idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
if (!idbObjectStore) {
m_requestCallback->sendFailure("Could not get object store");
return;
}
TransactionActivator activator(idbTransaction.get());
RefPtr<IDBRequest> idbRequest;
if (auto* exec = context() ? context()->execState() : nullptr) {
auto result = idbObjectStore->clear(*exec);
ASSERT(!result.hasException());
if (result.hasException()) {
m_requestCallback->sendFailure(String::format("Could not clear object store '%s': %d", m_objectStoreName.utf8().data(), result.releaseException().code()));
return;
}
idbRequest = result.releaseReturnValue();
}
idbTransaction->addEventListener(eventNames().completeEvent, ClearObjectStoreListener::create(m_requestCallback.copyRef()), false);
}
RequestCallback& requestCallback() override { return m_requestCallback.get(); }
private:
const String m_objectStoreName;
Ref<ClearObjectStoreCallback> m_requestCallback;
};
void InspectorIndexedDBAgent::clearObjectStore(ErrorString& errorString, const String& securityOrigin, const String& databaseName, const String& objectStoreName, Ref<ClearObjectStoreCallback>&& requestCallback)
{
Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
Document* document = assertDocument(errorString, frame);
if (!document)
return;
IDBFactory* idbFactory = assertIDBFactory(errorString, document);
if (!idbFactory)
return;
Ref<ClearObjectStore> clearObjectStore = ClearObjectStore::create(document, objectStoreName, WTFMove(requestCallback));
clearObjectStore->start(idbFactory, document->securityOrigin(), databaseName);
}
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)