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