• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "core/inspector/InspectorIndexedDBAgent.h"
33 
34 #include "bindings/v8/ExceptionState.h"
35 #include "bindings/v8/ExceptionStatePlaceholder.h"
36 #include "bindings/v8/ScriptController.h"
37 #include "core/dom/DOMStringList.h"
38 #include "core/dom/Document.h"
39 #include "core/events/Event.h"
40 #include "core/events/EventListener.h"
41 #include "core/inspector/InjectedScript.h"
42 #include "core/inspector/InspectorPageAgent.h"
43 #include "core/inspector/InspectorState.h"
44 #include "core/frame/Frame.h"
45 #include "modules/indexeddb/DOMWindowIndexedDatabase.h"
46 #include "modules/indexeddb/IDBCursor.h"
47 #include "modules/indexeddb/IDBCursorWithValue.h"
48 #include "modules/indexeddb/IDBDatabase.h"
49 #include "modules/indexeddb/IDBFactory.h"
50 #include "modules/indexeddb/IDBIndex.h"
51 #include "modules/indexeddb/IDBKey.h"
52 #include "modules/indexeddb/IDBKeyPath.h"
53 #include "modules/indexeddb/IDBKeyRange.h"
54 #include "modules/indexeddb/IDBMetadata.h"
55 #include "modules/indexeddb/IDBObjectStore.h"
56 #include "modules/indexeddb/IDBOpenDBRequest.h"
57 #include "modules/indexeddb/IDBPendingTransactionMonitor.h"
58 #include "modules/indexeddb/IDBRequest.h"
59 #include "modules/indexeddb/IDBTransaction.h"
60 #include "platform/JSONValues.h"
61 #include "platform/weborigin/SecurityOrigin.h"
62 #include "wtf/Vector.h"
63 
64 using WebCore::TypeBuilder::Array;
65 using WebCore::TypeBuilder::IndexedDB::DatabaseWithObjectStores;
66 using WebCore::TypeBuilder::IndexedDB::DataEntry;
67 using WebCore::TypeBuilder::IndexedDB::Key;
68 using WebCore::TypeBuilder::IndexedDB::KeyPath;
69 using WebCore::TypeBuilder::IndexedDB::KeyRange;
70 using WebCore::TypeBuilder::IndexedDB::ObjectStore;
71 using WebCore::TypeBuilder::IndexedDB::ObjectStoreIndex;
72 
73 typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::RequestDatabaseNamesCallback RequestDatabaseNamesCallback;
74 typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::RequestDatabaseCallback RequestDatabaseCallback;
75 typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::RequestDataCallback RequestDataCallback;
76 typedef WebCore::InspectorBackendDispatcher::CallbackBase RequestCallback;
77 typedef WebCore::InspectorBackendDispatcher::IndexedDBCommandHandler::ClearObjectStoreCallback ClearObjectStoreCallback;
78 
79 namespace WebCore {
80 
81 namespace IndexedDBAgentState {
82 static const char indexedDBAgentEnabled[] = "indexedDBAgentEnabled";
83 };
84 
85 namespace {
86 
87 class GetDatabaseNamesCallback : public EventListener {
88     WTF_MAKE_NONCOPYABLE(GetDatabaseNamesCallback);
89 public:
create(PassRefPtr<RequestDatabaseNamesCallback> requestCallback,const String & securityOrigin)90     static PassRefPtr<GetDatabaseNamesCallback> create(PassRefPtr<RequestDatabaseNamesCallback> requestCallback, const String& securityOrigin)
91     {
92         return adoptRef(new GetDatabaseNamesCallback(requestCallback, securityOrigin));
93     }
94 
~GetDatabaseNamesCallback()95     virtual ~GetDatabaseNamesCallback() { }
96 
operator ==(const EventListener & other)97     virtual bool operator==(const EventListener& other) OVERRIDE
98     {
99         return this == &other;
100     }
101 
handleEvent(ExecutionContext *,Event * event)102     virtual void handleEvent(ExecutionContext*, Event* event) OVERRIDE
103     {
104         if (!m_requestCallback->isActive())
105             return;
106         if (event->type() != EventTypeNames::success) {
107             m_requestCallback->sendFailure("Unexpected event type.");
108             return;
109         }
110 
111         IDBRequest* idbRequest = static_cast<IDBRequest*>(event->target());
112         RefPtr<IDBAny> requestResult = idbRequest->resultAsAny();
113         if (requestResult->type() != IDBAny::DOMStringListType) {
114             m_requestCallback->sendFailure("Unexpected result type.");
115             return;
116         }
117 
118         RefPtr<DOMStringList> databaseNamesList = requestResult->domStringList();
119         RefPtr<TypeBuilder::Array<String> > databaseNames = TypeBuilder::Array<String>::create();
120         for (size_t i = 0; i < databaseNamesList->length(); ++i)
121             databaseNames->addItem(databaseNamesList->item(i));
122         m_requestCallback->sendSuccess(databaseNames.release());
123     }
124 
125 private:
GetDatabaseNamesCallback(PassRefPtr<RequestDatabaseNamesCallback> requestCallback,const String & securityOrigin)126     GetDatabaseNamesCallback(PassRefPtr<RequestDatabaseNamesCallback> requestCallback, const String& securityOrigin)
127         : EventListener(EventListener::CPPEventListenerType)
128         , m_requestCallback(requestCallback)
129         , m_securityOrigin(securityOrigin) { }
130     RefPtr<RequestDatabaseNamesCallback> m_requestCallback;
131     String m_securityOrigin;
132 };
133 
134 class ExecutableWithDatabase : public RefCounted<ExecutableWithDatabase> {
135 public:
ExecutableWithDatabase(ExecutionContext * context)136     ExecutableWithDatabase(ExecutionContext* context)
137         : m_context(context) { }
~ExecutableWithDatabase()138     virtual ~ExecutableWithDatabase() { };
139     void start(IDBFactory*, SecurityOrigin*, const String& databaseName);
140     virtual void execute(PassRefPtr<IDBDatabase>) = 0;
141     virtual RequestCallback* requestCallback() = 0;
context()142     ExecutionContext* context() { return m_context; };
143 private:
144     ExecutionContext* m_context;
145 };
146 
147 class OpenDatabaseCallback : public EventListener {
148 public:
create(ExecutableWithDatabase * executableWithDatabase)149     static PassRefPtr<OpenDatabaseCallback> create(ExecutableWithDatabase* executableWithDatabase)
150     {
151         return adoptRef(new OpenDatabaseCallback(executableWithDatabase));
152     }
153 
~OpenDatabaseCallback()154     virtual ~OpenDatabaseCallback() { }
155 
operator ==(const EventListener & other)156     virtual bool operator==(const EventListener& other) OVERRIDE
157     {
158         return this == &other;
159     }
160 
handleEvent(ExecutionContext *,Event * event)161     virtual void handleEvent(ExecutionContext*, Event* event) OVERRIDE
162     {
163         if (event->type() != EventTypeNames::success) {
164             m_executableWithDatabase->requestCallback()->sendFailure("Unexpected event type.");
165             return;
166         }
167 
168         IDBOpenDBRequest* idbOpenDBRequest = static_cast<IDBOpenDBRequest*>(event->target());
169         RefPtr<IDBAny> requestResult = idbOpenDBRequest->resultAsAny();
170         if (requestResult->type() != IDBAny::IDBDatabaseType) {
171             m_executableWithDatabase->requestCallback()->sendFailure("Unexpected result type.");
172             return;
173         }
174 
175         RefPtr<IDBDatabase> idbDatabase = requestResult->idbDatabase();
176         m_executableWithDatabase->execute(idbDatabase);
177         IDBPendingTransactionMonitor::deactivateNewTransactions();
178         idbDatabase->close();
179     }
180 
181 private:
OpenDatabaseCallback(ExecutableWithDatabase * executableWithDatabase)182     OpenDatabaseCallback(ExecutableWithDatabase* executableWithDatabase)
183         : EventListener(EventListener::CPPEventListenerType)
184         , m_executableWithDatabase(executableWithDatabase) { }
185     RefPtr<ExecutableWithDatabase> m_executableWithDatabase;
186 };
187 
start(IDBFactory * idbFactory,SecurityOrigin *,const String & databaseName)188 void ExecutableWithDatabase::start(IDBFactory* idbFactory, SecurityOrigin*, const String& databaseName)
189 {
190     RefPtr<OpenDatabaseCallback> callback = OpenDatabaseCallback::create(this);
191     TrackExceptionState exceptionState;
192     RefPtr<IDBOpenDBRequest> idbOpenDBRequest = idbFactory->open(context(), databaseName, exceptionState);
193     if (exceptionState.hadException()) {
194         requestCallback()->sendFailure("Could not open database.");
195         return;
196     }
197     idbOpenDBRequest->addEventListener(EventTypeNames::success, callback, false);
198 }
199 
transactionForDatabase(ExecutionContext * executionContext,IDBDatabase * idbDatabase,const String & objectStoreName,const String & mode=IDBTransaction::modeReadOnly ())200 static PassRefPtr<IDBTransaction> transactionForDatabase(ExecutionContext* executionContext, IDBDatabase* idbDatabase, const String& objectStoreName, const String& mode = IDBTransaction::modeReadOnly())
201 {
202     TrackExceptionState exceptionState;
203     RefPtr<IDBTransaction> idbTransaction = idbDatabase->transaction(executionContext, objectStoreName, mode, exceptionState);
204     if (exceptionState.hadException())
205         return 0;
206     return idbTransaction;
207 }
208 
objectStoreForTransaction(IDBTransaction * idbTransaction,const String & objectStoreName)209 static PassRefPtr<IDBObjectStore> objectStoreForTransaction(IDBTransaction* idbTransaction, const String& objectStoreName)
210 {
211     TrackExceptionState exceptionState;
212     RefPtr<IDBObjectStore> idbObjectStore = idbTransaction->objectStore(objectStoreName, exceptionState);
213     if (exceptionState.hadException())
214         return 0;
215     return idbObjectStore;
216 }
217 
indexForObjectStore(IDBObjectStore * idbObjectStore,const String & indexName)218 static PassRefPtr<IDBIndex> indexForObjectStore(IDBObjectStore* idbObjectStore, const String& indexName)
219 {
220     TrackExceptionState exceptionState;
221     RefPtr<IDBIndex> idbIndex = idbObjectStore->index(indexName, exceptionState);
222     if (exceptionState.hadException())
223         return 0;
224     return idbIndex;
225 }
226 
keyPathFromIDBKeyPath(const IDBKeyPath & idbKeyPath)227 static PassRefPtr<KeyPath> keyPathFromIDBKeyPath(const IDBKeyPath& idbKeyPath)
228 {
229     RefPtr<KeyPath> keyPath;
230     switch (idbKeyPath.type()) {
231     case IDBKeyPath::NullType:
232         keyPath = KeyPath::create().setType(KeyPath::Type::Null);
233         break;
234     case IDBKeyPath::StringType:
235         keyPath = KeyPath::create().setType(KeyPath::Type::String);
236         keyPath->setString(idbKeyPath.string());
237         break;
238     case IDBKeyPath::ArrayType: {
239         keyPath = KeyPath::create().setType(KeyPath::Type::Array);
240         RefPtr<TypeBuilder::Array<String> > array = TypeBuilder::Array<String>::create();
241         const Vector<String>& stringArray = idbKeyPath.array();
242         for (size_t i = 0; i < stringArray.size(); ++i)
243             array->addItem(stringArray[i]);
244         keyPath->setArray(array);
245         break;
246     }
247     default:
248         ASSERT_NOT_REACHED();
249     }
250 
251     return keyPath.release();
252 }
253 
254 class DatabaseLoader : public ExecutableWithDatabase {
255 public:
create(ExecutionContext * context,PassRefPtr<RequestDatabaseCallback> requestCallback)256     static PassRefPtr<DatabaseLoader> create(ExecutionContext* context, PassRefPtr<RequestDatabaseCallback> requestCallback)
257     {
258         return adoptRef(new DatabaseLoader(context, requestCallback));
259     }
260 
~DatabaseLoader()261     virtual ~DatabaseLoader() { }
262 
execute(PassRefPtr<IDBDatabase> prpDatabase)263     virtual void execute(PassRefPtr<IDBDatabase> prpDatabase)
264     {
265         RefPtr<IDBDatabase> idbDatabase = prpDatabase;
266         if (!requestCallback()->isActive())
267             return;
268 
269         const IDBDatabaseMetadata databaseMetadata = idbDatabase->metadata();
270 
271         RefPtr<TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStore> > objectStores = TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStore>::create();
272 
273         for (IDBDatabaseMetadata::ObjectStoreMap::const_iterator it = databaseMetadata.objectStores.begin(); it != databaseMetadata.objectStores.end(); ++it) {
274             const IDBObjectStoreMetadata& objectStoreMetadata = it->value;
275 
276             RefPtr<TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStoreIndex> > indexes = TypeBuilder::Array<TypeBuilder::IndexedDB::ObjectStoreIndex>::create();
277 
278             for (IDBObjectStoreMetadata::IndexMap::const_iterator it = objectStoreMetadata.indexes.begin(); it != objectStoreMetadata.indexes.end(); ++it) {
279                 const IDBIndexMetadata& indexMetadata = it->value;
280 
281                 RefPtr<ObjectStoreIndex> objectStoreIndex = ObjectStoreIndex::create()
282                     .setName(indexMetadata.name)
283                     .setKeyPath(keyPathFromIDBKeyPath(indexMetadata.keyPath))
284                     .setUnique(indexMetadata.unique)
285                     .setMultiEntry(indexMetadata.multiEntry);
286                 indexes->addItem(objectStoreIndex);
287             }
288 
289             RefPtr<ObjectStore> objectStore = ObjectStore::create()
290                 .setName(objectStoreMetadata.name)
291                 .setKeyPath(keyPathFromIDBKeyPath(objectStoreMetadata.keyPath))
292                 .setAutoIncrement(objectStoreMetadata.autoIncrement)
293                 .setIndexes(indexes);
294             objectStores->addItem(objectStore);
295         }
296         RefPtr<DatabaseWithObjectStores> result = DatabaseWithObjectStores::create()
297             .setName(databaseMetadata.name)
298             .setIntVersion(databaseMetadata.intVersion)
299             .setVersion(databaseMetadata.version)
300             .setObjectStores(objectStores);
301 
302         m_requestCallback->sendSuccess(result);
303     }
304 
requestCallback()305     virtual RequestCallback* requestCallback() { return m_requestCallback.get(); }
306 private:
DatabaseLoader(ExecutionContext * context,PassRefPtr<RequestDatabaseCallback> requestCallback)307     DatabaseLoader(ExecutionContext* context, PassRefPtr<RequestDatabaseCallback> requestCallback)
308         : ExecutableWithDatabase(context)
309         , m_requestCallback(requestCallback) { }
310     RefPtr<RequestDatabaseCallback> m_requestCallback;
311 };
312 
idbKeyFromInspectorObject(JSONObject * key)313 static PassRefPtr<IDBKey> idbKeyFromInspectorObject(JSONObject* key)
314 {
315     RefPtr<IDBKey> idbKey;
316 
317     String type;
318     if (!key->getString("type", &type))
319         return 0;
320 
321     DEFINE_STATIC_LOCAL(String, number, ("number"));
322     DEFINE_STATIC_LOCAL(String, string, ("string"));
323     DEFINE_STATIC_LOCAL(String, date, ("date"));
324     DEFINE_STATIC_LOCAL(String, array, ("array"));
325 
326     if (type == number) {
327         double number;
328         if (!key->getNumber("number", &number))
329             return 0;
330         idbKey = IDBKey::createNumber(number);
331     } else if (type == string) {
332         String string;
333         if (!key->getString("string", &string))
334             return 0;
335         idbKey = IDBKey::createString(string);
336     } else if (type == date) {
337         double date;
338         if (!key->getNumber("date", &date))
339             return 0;
340         idbKey = IDBKey::createDate(date);
341     } else if (type == array) {
342         IDBKey::KeyArray keyArray;
343         RefPtr<JSONArray> array = key->getArray("array");
344         for (size_t i = 0; i < array->length(); ++i) {
345             RefPtr<JSONValue> value = array->get(i);
346             RefPtr<JSONObject> object;
347             if (!value->asObject(&object))
348                 return 0;
349             keyArray.append(idbKeyFromInspectorObject(object.get()));
350         }
351         idbKey = IDBKey::createArray(keyArray);
352     } else
353         return 0;
354 
355     return idbKey.release();
356 }
357 
idbKeyRangeFromKeyRange(JSONObject * keyRange)358 static PassRefPtr<IDBKeyRange> idbKeyRangeFromKeyRange(JSONObject* keyRange)
359 {
360     RefPtr<JSONObject> lower = keyRange->getObject("lower");
361     RefPtr<IDBKey> idbLower = lower ? idbKeyFromInspectorObject(lower.get()) : 0;
362     if (lower && !idbLower)
363         return 0;
364 
365     RefPtr<JSONObject> upper = keyRange->getObject("upper");
366     RefPtr<IDBKey> idbUpper = upper ? idbKeyFromInspectorObject(upper.get()) : 0;
367     if (upper && !idbUpper)
368         return 0;
369 
370     bool lowerOpen;
371     if (!keyRange->getBoolean("lowerOpen", &lowerOpen))
372         return 0;
373     IDBKeyRange::LowerBoundType lowerBoundType = lowerOpen ? IDBKeyRange::LowerBoundOpen : IDBKeyRange::LowerBoundClosed;
374 
375     bool upperOpen;
376     if (!keyRange->getBoolean("upperOpen", &upperOpen))
377         return 0;
378     IDBKeyRange::UpperBoundType upperBoundType = upperOpen ? IDBKeyRange::UpperBoundOpen : IDBKeyRange::UpperBoundClosed;
379 
380     RefPtr<IDBKeyRange> idbKeyRange = IDBKeyRange::create(idbLower, idbUpper, lowerBoundType, upperBoundType);
381     return idbKeyRange.release();
382 }
383 
384 class DataLoader;
385 
386 class OpenCursorCallback : public EventListener {
387 public:
create(InjectedScript injectedScript,PassRefPtr<RequestDataCallback> requestCallback,int skipCount,unsigned pageSize)388     static PassRefPtr<OpenCursorCallback> create(InjectedScript injectedScript, PassRefPtr<RequestDataCallback> requestCallback, int skipCount, unsigned pageSize)
389     {
390         return adoptRef(new OpenCursorCallback(injectedScript, requestCallback, skipCount, pageSize));
391     }
392 
~OpenCursorCallback()393     virtual ~OpenCursorCallback() { }
394 
operator ==(const EventListener & other)395     virtual bool operator==(const EventListener& other) OVERRIDE
396     {
397         return this == &other;
398     }
399 
handleEvent(ExecutionContext * context,Event * event)400     virtual void handleEvent(ExecutionContext* context, Event* event) OVERRIDE
401     {
402         if (event->type() != EventTypeNames::success) {
403             m_requestCallback->sendFailure("Unexpected event type.");
404             return;
405         }
406 
407         IDBRequest* idbRequest = static_cast<IDBRequest*>(event->target());
408         RefPtr<IDBAny> requestResult = idbRequest->resultAsAny();
409         if (requestResult->type() == IDBAny::BufferType) {
410             end(false);
411             return;
412         }
413         if (requestResult->type() != IDBAny::IDBCursorWithValueType) {
414             m_requestCallback->sendFailure("Unexpected result type.");
415             return;
416         }
417 
418         RefPtr<IDBCursorWithValue> idbCursor = requestResult->idbCursorWithValue();
419 
420         if (m_skipCount) {
421             TrackExceptionState exceptionState;
422             idbCursor->advance(m_skipCount, exceptionState);
423             if (exceptionState.hadException())
424                 m_requestCallback->sendFailure("Could not advance cursor.");
425             m_skipCount = 0;
426             return;
427         }
428 
429         if (m_result->length() == m_pageSize) {
430             end(true);
431             return;
432         }
433 
434         // Continue cursor before making injected script calls, otherwise transaction might be finished.
435         TrackExceptionState exceptionState;
436         idbCursor->continueFunction(0, 0, exceptionState);
437         if (exceptionState.hadException()) {
438             m_requestCallback->sendFailure("Could not continue cursor.");
439             return;
440         }
441 
442         RefPtr<DataEntry> dataEntry = DataEntry::create()
443             .setKey(m_injectedScript.wrapObject(idbCursor->key(context), String()))
444             .setPrimaryKey(m_injectedScript.wrapObject(idbCursor->primaryKey(context), String()))
445             .setValue(m_injectedScript.wrapObject(idbCursor->value(context), String()));
446         m_result->addItem(dataEntry);
447 
448     }
449 
end(bool hasMore)450     void end(bool hasMore)
451     {
452         if (!m_requestCallback->isActive())
453             return;
454         m_requestCallback->sendSuccess(m_result.release(), hasMore);
455     }
456 
457 private:
OpenCursorCallback(InjectedScript injectedScript,PassRefPtr<RequestDataCallback> requestCallback,int skipCount,unsigned pageSize)458     OpenCursorCallback(InjectedScript injectedScript, PassRefPtr<RequestDataCallback> requestCallback, int skipCount, unsigned pageSize)
459         : EventListener(EventListener::CPPEventListenerType)
460         , m_injectedScript(injectedScript)
461         , m_requestCallback(requestCallback)
462         , m_skipCount(skipCount)
463         , m_pageSize(pageSize)
464     {
465         m_result = Array<DataEntry>::create();
466     }
467     InjectedScript m_injectedScript;
468     RefPtr<RequestDataCallback> m_requestCallback;
469     int m_skipCount;
470     unsigned m_pageSize;
471     RefPtr<Array<DataEntry> > m_result;
472 };
473 
474 class DataLoader : public ExecutableWithDatabase {
475 public:
create(ExecutionContext * context,PassRefPtr<RequestDataCallback> requestCallback,const InjectedScript & injectedScript,const String & objectStoreName,const String & indexName,PassRefPtr<IDBKeyRange> idbKeyRange,int skipCount,unsigned pageSize)476     static PassRefPtr<DataLoader> create(ExecutionContext* context, PassRefPtr<RequestDataCallback> requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, PassRefPtr<IDBKeyRange> idbKeyRange, int skipCount, unsigned pageSize)
477     {
478         return adoptRef(new DataLoader(context, requestCallback, injectedScript, objectStoreName, indexName, idbKeyRange, skipCount, pageSize));
479     }
480 
~DataLoader()481     virtual ~DataLoader() { }
482 
execute(PassRefPtr<IDBDatabase> prpDatabase)483     virtual void execute(PassRefPtr<IDBDatabase> prpDatabase)
484     {
485         RefPtr<IDBDatabase> idbDatabase = prpDatabase;
486         if (!requestCallback()->isActive())
487             return;
488         RefPtr<IDBTransaction> idbTransaction = transactionForDatabase(context(), idbDatabase.get(), m_objectStoreName);
489         if (!idbTransaction) {
490             m_requestCallback->sendFailure("Could not get transaction");
491             return;
492         }
493         RefPtr<IDBObjectStore> idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
494         if (!idbObjectStore) {
495             m_requestCallback->sendFailure("Could not get object store");
496             return;
497         }
498 
499         RefPtr<OpenCursorCallback> openCursorCallback = OpenCursorCallback::create(m_injectedScript, m_requestCallback, m_skipCount, m_pageSize);
500 
501         RefPtr<IDBRequest> idbRequest;
502         if (!m_indexName.isEmpty()) {
503             RefPtr<IDBIndex> idbIndex = indexForObjectStore(idbObjectStore.get(), m_indexName);
504             if (!idbIndex) {
505                 m_requestCallback->sendFailure("Could not get index");
506                 return;
507             }
508 
509             idbRequest = idbIndex->openCursor(context(), PassRefPtr<IDBKeyRange>(m_idbKeyRange), IndexedDB::CursorNext);
510         } else {
511             idbRequest = idbObjectStore->openCursor(context(), PassRefPtr<IDBKeyRange>(m_idbKeyRange), IndexedDB::CursorNext);
512         }
513         idbRequest->addEventListener(EventTypeNames::success, openCursorCallback, false);
514     }
515 
requestCallback()516     virtual RequestCallback* requestCallback() { return m_requestCallback.get(); }
DataLoader(ExecutionContext * executionContext,PassRefPtr<RequestDataCallback> requestCallback,const InjectedScript & injectedScript,const String & objectStoreName,const String & indexName,PassRefPtr<IDBKeyRange> idbKeyRange,int skipCount,unsigned pageSize)517     DataLoader(ExecutionContext* executionContext, PassRefPtr<RequestDataCallback> requestCallback, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, PassRefPtr<IDBKeyRange> idbKeyRange, int skipCount, unsigned pageSize)
518         : ExecutableWithDatabase(executionContext)
519         , m_requestCallback(requestCallback)
520         , m_injectedScript(injectedScript)
521         , m_objectStoreName(objectStoreName)
522         , m_indexName(indexName)
523         , m_idbKeyRange(idbKeyRange)
524         , m_skipCount(skipCount)
525         , m_pageSize(pageSize) { }
526     RefPtr<RequestDataCallback> m_requestCallback;
527     InjectedScript m_injectedScript;
528     String m_objectStoreName;
529     String m_indexName;
530     RefPtr<IDBKeyRange> m_idbKeyRange;
531     int m_skipCount;
532     unsigned m_pageSize;
533 };
534 
535 } // namespace
536 
InspectorIndexedDBAgent(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * state,InjectedScriptManager * injectedScriptManager,InspectorPageAgent * pageAgent)537 InspectorIndexedDBAgent::InspectorIndexedDBAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InjectedScriptManager* injectedScriptManager, InspectorPageAgent* pageAgent)
538     : InspectorBaseAgent<InspectorIndexedDBAgent>("IndexedDB", instrumentingAgents, state)
539     , m_injectedScriptManager(injectedScriptManager)
540     , m_pageAgent(pageAgent)
541 {
542 }
543 
~InspectorIndexedDBAgent()544 InspectorIndexedDBAgent::~InspectorIndexedDBAgent()
545 {
546 }
547 
clearFrontend()548 void InspectorIndexedDBAgent::clearFrontend()
549 {
550     disable(0);
551 }
552 
restore()553 void InspectorIndexedDBAgent::restore()
554 {
555     if (m_state->getBoolean(IndexedDBAgentState::indexedDBAgentEnabled)) {
556         ErrorString error;
557         enable(&error);
558     }
559 }
560 
enable(ErrorString *)561 void InspectorIndexedDBAgent::enable(ErrorString*)
562 {
563     m_state->setBoolean(IndexedDBAgentState::indexedDBAgentEnabled, true);
564 }
565 
disable(ErrorString *)566 void InspectorIndexedDBAgent::disable(ErrorString*)
567 {
568     m_state->setBoolean(IndexedDBAgentState::indexedDBAgentEnabled, false);
569 }
570 
assertDocument(ErrorString * errorString,Frame * frame)571 static Document* assertDocument(ErrorString* errorString, Frame* frame)
572 {
573     Document* document = frame ? frame->document() : 0;
574 
575     if (!document)
576         *errorString = "No document for given frame found";
577 
578     return document;
579 }
580 
assertIDBFactory(ErrorString * errorString,Document * document)581 static IDBFactory* assertIDBFactory(ErrorString* errorString, Document* document)
582 {
583     DOMWindow* domWindow = document->domWindow();
584     if (!domWindow) {
585         *errorString = "No IndexedDB factory for given frame found";
586         return 0;
587     }
588     IDBFactory* idbFactory = DOMWindowIndexedDatabase::indexedDB(domWindow);
589 
590     if (!idbFactory)
591         *errorString = "No IndexedDB factory for given frame found";
592 
593     return idbFactory;
594 }
595 
requestDatabaseNames(ErrorString * errorString,const String & securityOrigin,PassRefPtr<RequestDatabaseNamesCallback> requestCallback)596 void InspectorIndexedDBAgent::requestDatabaseNames(ErrorString* errorString, const String& securityOrigin, PassRefPtr<RequestDatabaseNamesCallback> requestCallback)
597 {
598     Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
599     Document* document = assertDocument(errorString, frame);
600     if (!document)
601         return;
602     IDBFactory* idbFactory = assertIDBFactory(errorString, document);
603     if (!idbFactory)
604         return;
605 
606     // FIXME: This should probably use ScriptState/ScriptScope instead of V8 API
607     v8::HandleScope handleScope(toIsolate(frame));
608     v8::Handle<v8::Context> context = document->frame()->script().mainWorldContext();
609     ASSERT(!context.IsEmpty());
610     v8::Context::Scope contextScope(context);
611 
612     TrackExceptionState exceptionState;
613     RefPtr<IDBRequest> idbRequest = idbFactory->getDatabaseNames(document, exceptionState);
614     if (exceptionState.hadException()) {
615         requestCallback->sendFailure("Could not obtain database names.");
616         return;
617     }
618     idbRequest->addEventListener(EventTypeNames::success, GetDatabaseNamesCallback::create(requestCallback, document->securityOrigin()->toRawString()), false);
619 }
620 
requestDatabase(ErrorString * errorString,const String & securityOrigin,const String & databaseName,PassRefPtr<RequestDatabaseCallback> requestCallback)621 void InspectorIndexedDBAgent::requestDatabase(ErrorString* errorString, const String& securityOrigin, const String& databaseName, PassRefPtr<RequestDatabaseCallback> requestCallback)
622 {
623     Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
624     Document* document = assertDocument(errorString, frame);
625     if (!document)
626         return;
627     IDBFactory* idbFactory = assertIDBFactory(errorString, document);
628     if (!idbFactory)
629         return;
630 
631     // FIXME: This should probably use ScriptState/ScriptScope instead of V8 API
632     v8::HandleScope handleScope(toIsolate(frame));
633     v8::Handle<v8::Context> context = document->frame()->script().mainWorldContext();
634     ASSERT(!context.IsEmpty());
635     v8::Context::Scope contextScope(context);
636 
637     RefPtr<DatabaseLoader> databaseLoader = DatabaseLoader::create(document, requestCallback);
638     databaseLoader->start(idbFactory, document->securityOrigin(), databaseName);
639 }
640 
requestData(ErrorString * errorString,const String & securityOrigin,const String & databaseName,const String & objectStoreName,const String & indexName,int skipCount,int pageSize,const RefPtr<JSONObject> * keyRange,PassRefPtr<RequestDataCallback> requestCallback)641 void InspectorIndexedDBAgent::requestData(ErrorString* errorString, const String& securityOrigin, const String& databaseName, const String& objectStoreName, const String& indexName, int skipCount, int pageSize, const RefPtr<JSONObject>* keyRange, PassRefPtr<RequestDataCallback> requestCallback)
642 {
643     Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
644     Document* document = assertDocument(errorString, frame);
645     if (!document)
646         return;
647     IDBFactory* idbFactory = assertIDBFactory(errorString, document);
648     if (!idbFactory)
649         return;
650 
651     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
652 
653     RefPtr<IDBKeyRange> idbKeyRange = keyRange ? idbKeyRangeFromKeyRange(keyRange->get()) : 0;
654     if (keyRange && !idbKeyRange) {
655         *errorString = "Can not parse key range.";
656         return;
657     }
658 
659     // FIXME: This should probably use ScriptState/ScriptScope instead of V8 API
660     v8::HandleScope handleScope(toIsolate(frame));
661     v8::Handle<v8::Context> context = document->frame()->script().mainWorldContext();
662     ASSERT(!context.IsEmpty());
663     v8::Context::Scope contextScope(context);
664 
665     RefPtr<DataLoader> dataLoader = DataLoader::create(document, requestCallback, injectedScript, objectStoreName, indexName, idbKeyRange, skipCount, pageSize);
666     dataLoader->start(idbFactory, document->securityOrigin(), databaseName);
667 }
668 
669 class ClearObjectStoreListener : public EventListener {
670     WTF_MAKE_NONCOPYABLE(ClearObjectStoreListener);
671 public:
create(PassRefPtr<ClearObjectStoreCallback> requestCallback)672     static PassRefPtr<ClearObjectStoreListener> create(PassRefPtr<ClearObjectStoreCallback> requestCallback)
673     {
674         return adoptRef(new ClearObjectStoreListener(requestCallback));
675     }
676 
~ClearObjectStoreListener()677     virtual ~ClearObjectStoreListener() { }
678 
operator ==(const EventListener & other)679     virtual bool operator==(const EventListener& other) OVERRIDE
680     {
681         return this == &other;
682     }
683 
handleEvent(ExecutionContext *,Event * event)684     virtual void handleEvent(ExecutionContext*, Event* event) OVERRIDE
685     {
686         if (!m_requestCallback->isActive())
687             return;
688         if (event->type() != EventTypeNames::complete) {
689             m_requestCallback->sendFailure("Unexpected event type.");
690             return;
691         }
692 
693         m_requestCallback->sendSuccess();
694     }
695 private:
ClearObjectStoreListener(PassRefPtr<ClearObjectStoreCallback> requestCallback)696     ClearObjectStoreListener(PassRefPtr<ClearObjectStoreCallback> requestCallback)
697         : EventListener(EventListener::CPPEventListenerType)
698         , m_requestCallback(requestCallback)
699     {
700     }
701 
702     RefPtr<ClearObjectStoreCallback> m_requestCallback;
703 };
704 
705 
706 class ClearObjectStore : public ExecutableWithDatabase {
707 public:
create(ExecutionContext * context,const String & objectStoreName,PassRefPtr<ClearObjectStoreCallback> requestCallback)708     static PassRefPtr<ClearObjectStore> create(ExecutionContext* context, const String& objectStoreName, PassRefPtr<ClearObjectStoreCallback> requestCallback)
709     {
710         return adoptRef(new ClearObjectStore(context, objectStoreName, requestCallback));
711     }
712 
ClearObjectStore(ExecutionContext * context,const String & objectStoreName,PassRefPtr<ClearObjectStoreCallback> requestCallback)713     ClearObjectStore(ExecutionContext* context, const String& objectStoreName, PassRefPtr<ClearObjectStoreCallback> requestCallback)
714         : ExecutableWithDatabase(context)
715         , m_objectStoreName(objectStoreName)
716         , m_requestCallback(requestCallback)
717     {
718     }
719 
execute(PassRefPtr<IDBDatabase> prpDatabase)720     virtual void execute(PassRefPtr<IDBDatabase> prpDatabase)
721     {
722         RefPtr<IDBDatabase> idbDatabase = prpDatabase;
723         if (!requestCallback()->isActive())
724             return;
725         RefPtr<IDBTransaction> idbTransaction = transactionForDatabase(context(), idbDatabase.get(), m_objectStoreName, IDBTransaction::modeReadWrite());
726         if (!idbTransaction) {
727             m_requestCallback->sendFailure("Could not get transaction");
728             return;
729         }
730         RefPtr<IDBObjectStore> idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
731         if (!idbObjectStore) {
732             m_requestCallback->sendFailure("Could not get object store");
733             return;
734         }
735 
736         TrackExceptionState exceptionState;
737         RefPtr<IDBRequest> idbRequest = idbObjectStore->clear(context(), exceptionState);
738         ASSERT(!exceptionState.hadException());
739         if (exceptionState.hadException()) {
740             ExceptionCode ec = exceptionState.code();
741             m_requestCallback->sendFailure(String::format("Could not clear object store '%s': %d", m_objectStoreName.utf8().data(), ec));
742             return;
743         }
744         idbTransaction->addEventListener(EventTypeNames::complete, ClearObjectStoreListener::create(m_requestCallback), false);
745     }
746 
requestCallback()747     virtual RequestCallback* requestCallback() { return m_requestCallback.get(); }
748 private:
749     const String m_objectStoreName;
750     RefPtr<ClearObjectStoreCallback> m_requestCallback;
751 };
752 
clearObjectStore(ErrorString * errorString,const String & securityOrigin,const String & databaseName,const String & objectStoreName,PassRefPtr<ClearObjectStoreCallback> requestCallback)753 void InspectorIndexedDBAgent::clearObjectStore(ErrorString* errorString, const String& securityOrigin, const String& databaseName, const String& objectStoreName, PassRefPtr<ClearObjectStoreCallback> requestCallback)
754 {
755     Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
756     Document* document = assertDocument(errorString, frame);
757     if (!document)
758         return;
759     IDBFactory* idbFactory = assertIDBFactory(errorString, document);
760     if (!idbFactory)
761         return;
762 
763     // FIXME: This should probably use ScriptState/ScriptScope instead of V8 API
764     v8::HandleScope handleScope(toIsolate(frame));
765     v8::Handle<v8::Context> context = document->frame()->script().mainWorldContext();
766     ASSERT(!context.IsEmpty());
767     v8::Context::Scope contextScope(context);
768 
769     RefPtr<ClearObjectStore> clearObjectStore = ClearObjectStore::create(document, objectStoreName, requestCallback);
770     clearObjectStore->start(idbFactory, document->securityOrigin(), databaseName);
771 }
772 
773 } // namespace WebCore
774