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