1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "InspectorIndexedDBAgent.h"
34
35#if ENABLE(INDEXED_DATABASE)
36
37#include "DOMStringList.h"
38#include "DOMWindow.h"
39#include "DOMWindowIndexedDatabase.h"
40#include "Document.h"
41#include "Event.h"
42#include "EventListener.h"
43#include "EventNames.h"
44#include "EventTarget.h"
45#include "Frame.h"
46#include "IDBBindingUtilities.h"
47#include "IDBCursor.h"
48#include "IDBCursorWithValue.h"
49#include "IDBDatabase.h"
50#include "IDBFactory.h"
51#include "IDBIndex.h"
52#include "IDBKey.h"
53#include "IDBKeyPath.h"
54#include "IDBKeyRange.h"
55#include "IDBObjectStore.h"
56#include "IDBOpenDBRequest.h"
57#include "IDBRequest.h"
58#include "IDBTransaction.h"
59#include "InspectorPageAgent.h"
60#include "InstrumentingAgents.h"
61#include "ScriptState.h"
62#include "SecurityOrigin.h"
63#include <JavaScriptCore/HeapInlines.h>
64#include <JavaScriptCore/InjectedScript.h>
65#include <JavaScriptCore/InjectedScriptManager.h>
66#include <JavaScriptCore/InspectorFrontendDispatchers.h>
67#include <JavaScriptCore/InspectorFrontendRouter.h>
68#include <wtf/JSONValues.h>
69#include <wtf/NeverDestroyed.h>
70#include <wtf/Vector.h>
71#include <wtf/text/StringConcatenateNumbers.h>
72
73using JSON::ArrayOf;
74using Inspector::Protocol::IndexedDB::DatabaseWithObjectStores;
75using Inspector::Protocol::IndexedDB::DataEntry;
76using Inspector::Protocol::IndexedDB::Key;
77using Inspector::Protocol::IndexedDB::KeyPath;
78using Inspector::Protocol::IndexedDB::KeyRange;
79using Inspector::Protocol::IndexedDB::ObjectStore;
80using Inspector::Protocol::IndexedDB::ObjectStoreIndex;
81
82typedef Inspector::BackendDispatcher::CallbackBase RequestCallback;
83typedef Inspector::IndexedDBBackendDispatcherHandler::RequestDatabaseNamesCallback RequestDatabaseNamesCallback;
84typedef Inspector::IndexedDBBackendDispatcherHandler::RequestDatabaseCallback RequestDatabaseCallback;
85typedef Inspector::IndexedDBBackendDispatcherHandler::RequestDataCallback RequestDataCallback;
86typedef Inspector::IndexedDBBackendDispatcherHandler::ClearObjectStoreCallback ClearObjectStoreCallback;
87
88typedef String ErrorString;
89
90template <typename T>
91using ErrorStringOr = Expected<T, ErrorString>;
92
93namespace WebCore {
94
95using namespace Inspector;
96
97namespace {
98
99class ExecutableWithDatabase : public RefCounted<ExecutableWithDatabase> {
100public:
101 ExecutableWithDatabase(ScriptExecutionContext* context)
102 : m_context(context) { }
103 virtual ~ExecutableWithDatabase() = default;
104 void start(IDBFactory*, SecurityOrigin*, const String& databaseName);
105 virtual void execute(IDBDatabase&) = 0;
106 virtual RequestCallback& requestCallback() = 0;
107 ScriptExecutionContext* context() const { return m_context; }
108private:
109 ScriptExecutionContext* m_context;
110};
111
112class OpenDatabaseCallback final : public EventListener {
113public:
114 static Ref<OpenDatabaseCallback> create(ExecutableWithDatabase& executableWithDatabase)
115 {
116 return adoptRef(*new OpenDatabaseCallback(executableWithDatabase));
117 }
118
119 bool operator==(const EventListener& other) const final
120 {
121 return this == &other;
122 }
123
124 void handleEvent(ScriptExecutionContext&, Event& event) final
125 {
126 if (event.type() != eventNames().successEvent) {
127 m_executableWithDatabase->requestCallback().sendFailure("Unexpected event type.");
128 return;
129 }
130
131 auto& request = static_cast<IDBOpenDBRequest&>(*event.target());
132
133 auto result = request.result();
134 if (result.hasException()) {
135 m_executableWithDatabase->requestCallback().sendFailure("Could not get result in callback.");
136 return;
137 }
138
139 auto resultValue = result.releaseReturnValue();
140 if (!WTF::holds_alternative<RefPtr<IDBDatabase>>(resultValue)) {
141 m_executableWithDatabase->requestCallback().sendFailure("Unexpected result type.");
142 return;
143 }
144
145 auto databaseResult = WTF::get<RefPtr<IDBDatabase>>(resultValue);
146 m_executableWithDatabase->execute(*databaseResult);
147 databaseResult->close();
148 }
149
150private:
151 OpenDatabaseCallback(ExecutableWithDatabase& executableWithDatabase)
152 : EventListener(EventListener::CPPEventListenerType)
153 , m_executableWithDatabase(executableWithDatabase) { }
154 Ref<ExecutableWithDatabase> m_executableWithDatabase;
155};
156
157void ExecutableWithDatabase::start(IDBFactory* idbFactory, SecurityOrigin*, const String& databaseName)
158{
159 if (!context()) {
160 requestCallback().sendFailure("Could not open database.");
161 return;
162 }
163
164 auto result = idbFactory->open(*context(), databaseName, WTF::nullopt);
165 if (result.hasException()) {
166 requestCallback().sendFailure("Could not open database.");
167 return;
168 }
169
170 result.releaseReturnValue()->addEventListener(eventNames().successEvent, OpenDatabaseCallback::create(*this), false);
171}
172
173
174static RefPtr<KeyPath> keyPathFromIDBKeyPath(const Optional<IDBKeyPath>& idbKeyPath)
175{
176 if (!idbKeyPath)
177 return KeyPath::create().setType(KeyPath::Type::Null).release();
178
179 auto visitor = WTF::makeVisitor([](const String& string) {
180 auto keyPath = KeyPath::create().setType(KeyPath::Type::String).release();
181 keyPath->setString(string);
182 return keyPath;
183 }, [](const Vector<String>& vector) {
184 auto array = JSON::ArrayOf<String>::create();
185 for (auto& string : vector)
186 array->addItem(string);
187 auto keyPath = KeyPath::create().setType(KeyPath::Type::Array).release();
188 keyPath->setArray(WTFMove(array));
189 return keyPath;
190 });
191 return WTF::visit(visitor, idbKeyPath.value());
192}
193
194static RefPtr<IDBTransaction> transactionForDatabase(IDBDatabase* idbDatabase, const String& objectStoreName, IDBTransactionMode mode = IDBTransactionMode::Readonly)
195{
196 auto result = idbDatabase->transaction(objectStoreName, mode);
197 if (result.hasException())
198 return nullptr;
199 return result.releaseReturnValue();
200}
201
202static RefPtr<IDBObjectStore> objectStoreForTransaction(IDBTransaction* idbTransaction, const String& objectStoreName)
203{
204 auto result = idbTransaction->objectStore(objectStoreName);
205 if (result.hasException())
206 return nullptr;
207 return result.releaseReturnValue();
208}
209
210static RefPtr<IDBIndex> indexForObjectStore(IDBObjectStore* idbObjectStore, const String& indexName)
211{
212 auto index = idbObjectStore->index(indexName);
213 if (index.hasException())
214 return nullptr;
215 return index.releaseReturnValue();
216}
217
218class DatabaseLoader final : public ExecutableWithDatabase {
219public:
220 static Ref<DatabaseLoader> create(ScriptExecutionContext* context, Ref<RequestDatabaseCallback>&& requestCallback)
221 {
222 return adoptRef(*new DatabaseLoader(context, WTFMove(requestCallback)));
223 }
224
225 virtual ~DatabaseLoader() = default;
226
227 void execute(IDBDatabase& database) override
228 {
229 if (!requestCallback().isActive())
230 return;
231
232 auto& databaseInfo = database.info();
233 auto objectStores = JSON::ArrayOf<Inspector::Protocol::IndexedDB::ObjectStore>::create();
234 auto objectStoreNames = databaseInfo.objectStoreNames();
235 for (auto& name : objectStoreNames) {
236 auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(name);
237 if (!objectStoreInfo)
238 continue;
239
240 auto indexes = JSON::ArrayOf<Inspector::Protocol::IndexedDB::ObjectStoreIndex>::create();
241
242 for (auto& indexInfo : objectStoreInfo->indexMap().values()) {
243 auto objectStoreIndex = ObjectStoreIndex::create()
244 .setName(indexInfo.name())
245 .setKeyPath(keyPathFromIDBKeyPath(indexInfo.keyPath()))
246 .setUnique(indexInfo.unique())
247 .setMultiEntry(indexInfo.multiEntry())
248 .release();
249 indexes->addItem(WTFMove(objectStoreIndex));
250 }
251
252 auto objectStore = ObjectStore::create()
253 .setName(objectStoreInfo->name())
254 .setKeyPath(keyPathFromIDBKeyPath(objectStoreInfo->keyPath()))
255 .setAutoIncrement(objectStoreInfo->autoIncrement())
256 .setIndexes(WTFMove(indexes))
257 .release();
258 objectStores->addItem(WTFMove(objectStore));
259 }
260
261 auto result = DatabaseWithObjectStores::create()
262 .setName(databaseInfo.name())
263 .setVersion(databaseInfo.version())
264 .setObjectStores(WTFMove(objectStores))
265 .release();
266 m_requestCallback->sendSuccess(WTFMove(result));
267 }
268
269 RequestCallback& requestCallback() override { return m_requestCallback.get(); }
270private:
271 DatabaseLoader(ScriptExecutionContext* context, Ref<RequestDatabaseCallback>&& requestCallback)
272 : ExecutableWithDatabase(context)
273 , m_requestCallback(WTFMove(requestCallback)) { }
274 Ref<RequestDatabaseCallback> m_requestCallback;
275};
276
277static RefPtr<IDBKey> idbKeyFromInspectorObject(JSON::Object* key)
278{
279 String type;
280 if (!key->getString("type", type))
281 return nullptr;
282
283 static NeverDestroyed<const String> numberType(MAKE_STATIC_STRING_IMPL("number"));
284 static NeverDestroyed<const String> stringType(MAKE_STATIC_STRING_IMPL("string"));
285 static NeverDestroyed<const String> dateType(MAKE_STATIC_STRING_IMPL("date"));
286 static NeverDestroyed<const String> arrayType(MAKE_STATIC_STRING_IMPL("array"));
287
288 RefPtr<IDBKey> idbKey;
289 if (type == numberType) {
290 double number;
291 if (!key->getDouble("number", number))
292 return nullptr;
293 idbKey = IDBKey::createNumber(number);
294 } else if (type == stringType) {
295 String string;
296 if (!key->getString("string", string))
297 return nullptr;
298 idbKey = IDBKey::createString(string);
299 } else if (type == dateType) {
300 double date;
301 if (!key->getDouble("date", date))
302 return nullptr;
303 idbKey = IDBKey::createDate(date);
304 } else if (type == arrayType) {
305 Vector<RefPtr<IDBKey>> keyArray;
306 RefPtr<JSON::Array> array;
307 if (!key->getArray("array", array))
308 return nullptr;
309 for (size_t i = 0; i < array->length(); ++i) {
310 RefPtr<JSON::Value> value = array->get(i);
311 RefPtr<JSON::Object> object;
312 if (!value->asObject(object))
313 return nullptr;
314 keyArray.append(idbKeyFromInspectorObject(object.get()));
315 }
316 idbKey = IDBKey::createArray(keyArray);
317 } else
318 return nullptr;
319
320 return idbKey;
321}
322
323static RefPtr<IDBKeyRange> idbKeyRangeFromKeyRange(const JSON::Object* keyRange)
324{
325 RefPtr<IDBKey> idbLower;
326 RefPtr<JSON::Object> lower;
327 if (keyRange->getObject("lower"_s, lower)) {
328 idbLower = idbKeyFromInspectorObject(lower.get());
329 if (!idbLower)
330 return nullptr;
331 }
332
333 RefPtr<IDBKey> idbUpper;
334 RefPtr<JSON::Object> upper;
335 if (keyRange->getObject("upper"_s, upper)) {
336 idbUpper = idbKeyFromInspectorObject(upper.get());
337 if (!idbUpper)
338 return nullptr;
339 }
340
341 bool lowerOpen;
342 if (!keyRange->getBoolean("lowerOpen"_s, lowerOpen))
343 return nullptr;
344
345 bool upperOpen;
346 if (!keyRange->getBoolean("upperOpen"_s, upperOpen))
347 return nullptr;
348
349 return IDBKeyRange::create(WTFMove(idbLower), WTFMove(idbUpper), lowerOpen, upperOpen);
350}
351
352class OpenCursorCallback final : public EventListener {
353public:
354 static Ref<OpenCursorCallback> create(InjectedScript injectedScript, Ref<RequestDataCallback>&& requestCallback, int skipCount, unsigned pageSize)
355 {
356 return adoptRef(*new OpenCursorCallback(injectedScript, WTFMove(requestCallback), skipCount, pageSize));
357 }
358
359 virtual ~OpenCursorCallback() = default;
360
361 bool operator==(const EventListener& other) const override
362 {
363 return this == &other;
364 }
365
366 void handleEvent(ScriptExecutionContext& context, Event& event) override
367 {
368 if (event.type() != eventNames().successEvent) {
369 m_requestCallback->sendFailure("Unexpected event type.");
370 return;
371 }
372
373 auto& request = static_cast<IDBRequest&>(*event.target());
374
375 auto result = request.result();
376 if (result.hasException()) {
377 m_requestCallback->sendFailure("Could not get result in callback.");
378 return;
379 }
380
381 auto resultValue = result.releaseReturnValue();
382 if (!WTF::holds_alternative<RefPtr<IDBCursor>>(resultValue)) {
383 end(false);
384 return;
385 }
386
387 auto cursor = WTF::get<RefPtr<IDBCursor>>(resultValue);
388
389 if (m_skipCount) {
390 if (cursor->advance(m_skipCount).hasException())
391 m_requestCallback->sendFailure("Could not advance cursor.");
392 m_skipCount = 0;
393 return;
394 }
395
396 if (m_result->length() == m_pageSize) {
397 end(true);
398 return;
399 }
400
401 // Continue cursor before making injected script calls, otherwise transaction might be finished.
402 if (cursor->continueFunction(nullptr).hasException()) {
403 m_requestCallback->sendFailure("Could not continue cursor.");
404 return;
405 }
406
407 auto* state = context.execState();
408 auto key = toJS(*state, *state->lexicalGlobalObject(), cursor->key());
409 auto primaryKey = toJS(*state, *state->lexicalGlobalObject(), cursor->primaryKey());
410 auto value = deserializeIDBValueToJSValue(*state, cursor->value());
411 auto dataEntry = DataEntry::create()
412 .setKey(m_injectedScript.wrapObject(key, String(), true))
413 .setPrimaryKey(m_injectedScript.wrapObject(primaryKey, String(), true))
414 .setValue(m_injectedScript.wrapObject(value, String(), true))
415 .release();
416 m_result->addItem(WTFMove(dataEntry));
417 }
418
419 void end(bool hasMore)
420 {
421 if (!m_requestCallback->isActive())
422 return;
423 m_requestCallback->sendSuccess(WTFMove(m_result), hasMore);
424 }
425
426private:
427 OpenCursorCallback(InjectedScript injectedScript, Ref<RequestDataCallback>&& requestCallback, int skipCount, unsigned pageSize)
428 : EventListener(EventListener::CPPEventListenerType)
429 , m_injectedScript(injectedScript)
430 , m_requestCallback(WTFMove(requestCallback))
431 , m_result(JSON::ArrayOf<DataEntry>::create())
432 , m_skipCount(skipCount)
433 , m_pageSize(pageSize)
434 {
435 }
436 InjectedScript m_injectedScript;
437 Ref<RequestDataCallback> m_requestCallback;
438 Ref<JSON::ArrayOf<DataEntry>> m_result;
439 int m_skipCount;
440 unsigned m_pageSize;
441};
442
443class DataLoader final : public ExecutableWithDatabase {
444public:
445 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)
446 {
447 return adoptRef(*new DataLoader(context, WTFMove(requestCallback), injectedScript, objectStoreName, indexName, WTFMove(idbKeyRange), skipCount, pageSize));
448 }
449
450 virtual ~DataLoader() = default;
451
452 void execute(IDBDatabase& database) override
453 {
454 if (!requestCallback().isActive())
455 return;
456
457 auto idbTransaction = transactionForDatabase(&database, m_objectStoreName);
458 if (!idbTransaction) {
459 m_requestCallback->sendFailure("Could not get transaction");
460 return;
461 }
462
463 auto idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
464 if (!idbObjectStore) {
465 m_requestCallback->sendFailure("Could not get object store");
466 return;
467 }
468
469 TransactionActivator activator(idbTransaction.get());
470 RefPtr<IDBRequest> idbRequest;
471 auto* exec = context() ? context()->execState() : nullptr;
472 if (!m_indexName.isEmpty()) {
473 auto idbIndex = indexForObjectStore(idbObjectStore.get(), m_indexName);
474 if (!idbIndex) {
475 m_requestCallback->sendFailure("Could not get index");
476 return;
477 }
478
479 if (exec) {
480 auto result = idbIndex->openCursor(*exec, m_idbKeyRange.get(), IDBCursorDirection::Next);
481 if (!result.hasException())
482 idbRequest = result.releaseReturnValue();
483 }
484 } else {
485 if (exec) {
486 auto result = idbObjectStore->openCursor(*exec, m_idbKeyRange.get(), IDBCursorDirection::Next);
487 if (!result.hasException())
488 idbRequest = result.releaseReturnValue();
489 }
490 }
491
492 if (!idbRequest) {
493 m_requestCallback->sendFailure("Could not open cursor to populate database data");
494 return;
495 }
496
497 auto openCursorCallback = OpenCursorCallback::create(m_injectedScript, m_requestCallback.copyRef(), m_skipCount, m_pageSize);
498 idbRequest->addEventListener(eventNames().successEvent, WTFMove(openCursorCallback), false);
499 }
500
501 RequestCallback& requestCallback() override { return m_requestCallback.get(); }
502 DataLoader(ScriptExecutionContext* scriptExecutionContext, Ref<RequestDataCallback>&& requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, RefPtr<IDBKeyRange> idbKeyRange, int skipCount, unsigned pageSize)
503 : ExecutableWithDatabase(scriptExecutionContext)
504 , m_requestCallback(WTFMove(requestCallback))
505 , m_injectedScript(injectedScript)
506 , m_objectStoreName(objectStoreName)
507 , m_indexName(indexName)
508 , m_idbKeyRange(WTFMove(idbKeyRange))
509 , m_skipCount(skipCount)
510 , m_pageSize(pageSize) { }
511 Ref<RequestDataCallback> m_requestCallback;
512 InjectedScript m_injectedScript;
513 String m_objectStoreName;
514 String m_indexName;
515 RefPtr<IDBKeyRange> m_idbKeyRange;
516 int m_skipCount;
517 unsigned m_pageSize;
518};
519
520} // namespace
521
522InspectorIndexedDBAgent::InspectorIndexedDBAgent(PageAgentContext& context)
523 : InspectorAgentBase("IndexedDB"_s, context)
524 , m_injectedScriptManager(context.injectedScriptManager)
525 , m_backendDispatcher(Inspector::IndexedDBBackendDispatcher::create(context.backendDispatcher, this))
526 , m_inspectedPage(context.inspectedPage)
527{
528}
529
530void InspectorIndexedDBAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
531{
532}
533
534void InspectorIndexedDBAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
535{
536 ErrorString unused;
537 disable(unused);
538}
539
540void InspectorIndexedDBAgent::enable(ErrorString&)
541{
542}
543
544void InspectorIndexedDBAgent::disable(ErrorString&)
545{
546}
547
548static ErrorStringOr<Document*> documentFromFrame(Frame* frame)
549{
550 Document* document = frame ? frame->document() : nullptr;
551 if (!document)
552 return makeUnexpected("No document for given frame found"_s);
553
554 return document;
555}
556
557static ErrorStringOr<IDBFactory*> IDBFactoryFromDocument(Document* document)
558{
559 DOMWindow* domWindow = document->domWindow();
560 if (!domWindow)
561 return makeUnexpected("No IndexedDB factory for given frame found"_s);
562
563 IDBFactory* idbFactory = DOMWindowIndexedDatabase::indexedDB(*domWindow);
564 if (!idbFactory)
565 makeUnexpected("No IndexedDB factory for given frame found"_s);
566
567 return idbFactory;
568}
569
570static bool getDocumentAndIDBFactoryFromFrameOrSendFailure(Frame* frame, Document*& out_document, IDBFactory*& out_idbFactory, BackendDispatcher::CallbackBase& callback)
571{
572 ErrorStringOr<Document*> document = documentFromFrame(frame);
573 if (!document.has_value()) {
574 callback.sendFailure(document.error());
575 return false;
576 }
577
578 ErrorStringOr<IDBFactory*> idbFactory = IDBFactoryFromDocument(document.value());
579 if (!idbFactory.has_value()) {
580 callback.sendFailure(idbFactory.error());
581 return false;
582 }
583
584 out_document = document.value();
585 out_idbFactory = idbFactory.value();
586 return true;
587}
588
589void InspectorIndexedDBAgent::requestDatabaseNames(const String& securityOrigin, Ref<RequestDatabaseNamesCallback>&& callback)
590{
591 auto* frame = InspectorPageAgent::findFrameWithSecurityOrigin(m_inspectedPage, securityOrigin);
592 Document* document;
593 IDBFactory* idbFactory;
594 if (!getDocumentAndIDBFactoryFromFrameOrSendFailure(frame, document, idbFactory, callback))
595 return;
596
597 auto& openingOrigin = document->securityOrigin();
598 auto& topOrigin = document->topOrigin();
599 idbFactory->getAllDatabaseNames(topOrigin, openingOrigin, [callback = WTFMove(callback)](auto& databaseNames) {
600 if (!callback->isActive())
601 return;
602
603 Ref<JSON::ArrayOf<String>> databaseNameArray = JSON::ArrayOf<String>::create();
604 for (auto& databaseName : databaseNames)
605 databaseNameArray->addItem(databaseName);
606
607 callback->sendSuccess(WTFMove(databaseNameArray));
608 });
609}
610
611void InspectorIndexedDBAgent::requestDatabase(const String& securityOrigin, const String& databaseName, Ref<RequestDatabaseCallback>&& callback)
612{
613 auto* frame = InspectorPageAgent::findFrameWithSecurityOrigin(m_inspectedPage, securityOrigin);
614 Document* document;
615 IDBFactory* idbFactory;
616 if (!getDocumentAndIDBFactoryFromFrameOrSendFailure(frame, document, idbFactory, callback))
617 return;
618
619 Ref<DatabaseLoader> databaseLoader = DatabaseLoader::create(document, WTFMove(callback));
620 databaseLoader->start(idbFactory, &document->securityOrigin(), databaseName);
621}
622
623void InspectorIndexedDBAgent::requestData(const String& securityOrigin, const String& databaseName, const String& objectStoreName, const String& indexName, int skipCount, int pageSize, const JSON::Object* keyRange, Ref<RequestDataCallback>&& callback)
624{
625 auto* frame = InspectorPageAgent::findFrameWithSecurityOrigin(m_inspectedPage, securityOrigin);
626 Document* document;
627 IDBFactory* idbFactory;
628 if (!getDocumentAndIDBFactoryFromFrameOrSendFailure(frame, document, idbFactory, callback))
629 return;
630
631 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(mainWorldExecState(frame));
632 RefPtr<IDBKeyRange> idbKeyRange = keyRange ? idbKeyRangeFromKeyRange(keyRange) : nullptr;
633 if (keyRange && !idbKeyRange) {
634 callback->sendFailure("Can not parse key range."_s);
635 return;
636 }
637
638 Ref<DataLoader> dataLoader = DataLoader::create(document, WTFMove(callback), injectedScript, objectStoreName, indexName, WTFMove(idbKeyRange), skipCount, pageSize);
639 dataLoader->start(idbFactory, &document->securityOrigin(), databaseName);
640}
641
642namespace {
643
644class ClearObjectStoreListener final : public EventListener {
645 WTF_MAKE_NONCOPYABLE(ClearObjectStoreListener);
646public:
647 static Ref<ClearObjectStoreListener> create(Ref<ClearObjectStoreCallback> requestCallback)
648 {
649 return adoptRef(*new ClearObjectStoreListener(WTFMove(requestCallback)));
650 }
651
652 virtual ~ClearObjectStoreListener() = default;
653
654 bool operator==(const EventListener& other) const override
655 {
656 return this == &other;
657 }
658
659 void handleEvent(ScriptExecutionContext&, Event& event) override
660 {
661 if (!m_requestCallback->isActive())
662 return;
663 if (event.type() != eventNames().completeEvent) {
664 m_requestCallback->sendFailure("Unexpected event type.");
665 return;
666 }
667
668 m_requestCallback->sendSuccess();
669 }
670private:
671 ClearObjectStoreListener(Ref<ClearObjectStoreCallback>&& requestCallback)
672 : EventListener(EventListener::CPPEventListenerType)
673 , m_requestCallback(WTFMove(requestCallback))
674 {
675 }
676
677 Ref<ClearObjectStoreCallback> m_requestCallback;
678};
679
680class ClearObjectStore final : public ExecutableWithDatabase {
681public:
682 static Ref<ClearObjectStore> create(ScriptExecutionContext* context, const String& objectStoreName, Ref<ClearObjectStoreCallback>&& requestCallback)
683 {
684 return adoptRef(*new ClearObjectStore(context, objectStoreName, WTFMove(requestCallback)));
685 }
686
687 ClearObjectStore(ScriptExecutionContext* context, const String& objectStoreName, Ref<ClearObjectStoreCallback>&& requestCallback)
688 : ExecutableWithDatabase(context)
689 , m_objectStoreName(objectStoreName)
690 , m_requestCallback(WTFMove(requestCallback))
691 {
692 }
693
694 void execute(IDBDatabase& database) override
695 {
696 if (!requestCallback().isActive())
697 return;
698
699 auto idbTransaction = transactionForDatabase(&database, m_objectStoreName, IDBTransactionMode::Readwrite);
700 if (!idbTransaction) {
701 m_requestCallback->sendFailure("Could not get transaction");
702 return;
703 }
704
705 auto idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
706 if (!idbObjectStore) {
707 m_requestCallback->sendFailure("Could not get object store");
708 return;
709 }
710
711 TransactionActivator activator(idbTransaction.get());
712 RefPtr<IDBRequest> idbRequest;
713 if (auto* exec = context() ? context()->execState() : nullptr) {
714 auto result = idbObjectStore->clear(*exec);
715 ASSERT(!result.hasException());
716 if (result.hasException()) {
717 m_requestCallback->sendFailure(makeString("Could not clear object store '", m_objectStoreName, "': ", static_cast<int>(result.releaseException().code())));
718 return;
719 }
720 idbRequest = result.releaseReturnValue();
721 }
722
723 idbTransaction->addEventListener(eventNames().completeEvent, ClearObjectStoreListener::create(m_requestCallback.copyRef()), false);
724 }
725
726 RequestCallback& requestCallback() override { return m_requestCallback.get(); }
727private:
728 const String m_objectStoreName;
729 Ref<ClearObjectStoreCallback> m_requestCallback;
730};
731
732} // anonymous namespace
733
734void InspectorIndexedDBAgent::clearObjectStore(const String& securityOrigin, const String& databaseName, const String& objectStoreName, Ref<ClearObjectStoreCallback>&& callback)
735{
736 auto* frame = InspectorPageAgent::findFrameWithSecurityOrigin(m_inspectedPage, securityOrigin);
737 Document* document;
738 IDBFactory* idbFactory;
739 if (!getDocumentAndIDBFactoryFromFrameOrSendFailure(frame, document, idbFactory, callback))
740 return;
741
742 Ref<ClearObjectStore> clearObjectStore = ClearObjectStore::create(document, objectStoreName, WTFMove(callback));
743 clearObjectStore->start(idbFactory, &document->securityOrigin(), databaseName);
744}
745
746} // namespace WebCore
747#endif // ENABLE(INDEXED_DATABASE)
748