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