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