Web Inspector: [InspectorIndexedDB] Pass data entries from object stores and indexes...
authorvsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Feb 2012 12:19:12 +0000 (12:19 +0000)
committervsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Feb 2012 12:19:12 +0000 (12:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78503

Reviewed by Yury Semikhatsky.

Source/WebCore:

Test: http/tests/inspector/indexeddb/database-data.html

* bindings/js/SerializedScriptValue.cpp:
(WebCore::SerializedScriptValue::deserializeForInspector):
(WebCore):
* bindings/js/SerializedScriptValue.h:
(SerializedScriptValue):
* bindings/v8/SerializedScriptValue.cpp:
(WebCore::SerializedScriptValue::deserializeForInspector):
(WebCore):
* bindings/v8/SerializedScriptValue.h:
(SerializedScriptValue):
* inspector/InjectedScript.cpp:
(WebCore::InjectedScript::wrapObject):
(WebCore::InjectedScript::wrapSerializedObject):
(WebCore):
(WebCore::InjectedScript::canAccessInspectedWindow):
* inspector/InjectedScript.h:
(InjectedScript):
* inspector/Inspector.json:
* inspector/InspectorController.cpp:
(WebCore::InspectorController::InspectorController):
* inspector/InspectorIndexedDBAgent.cpp:
(WebCore):
(WebCore::InspectorIndexedDBAgent::InspectorIndexedDBAgent):
(WebCore::assertFrame):
(WebCore::assertDocument):
(WebCore::InspectorIndexedDBAgent::requestData):
* inspector/InspectorIndexedDBAgent.h:
(WebCore):
(WebCore::InspectorIndexedDBAgent::create):
(InspectorIndexedDBAgent):
* inspector/front-end/IndexedDBModel.js:
(WebInspector.IndexedDBModel.idbKeyFromKey):
(WebInspector.IndexedDBModel.keyFromIDBKey):
(WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange):
(WebInspector.IndexedDBModel.prototype._loadDatabase):
(WebInspector.IndexedDBModel.prototype.loadObjectStoreData):
(WebInspector.IndexedDBModel.prototype.loadIndexData):
(WebInspector.IndexedDBModel.Entry):
(WebInspector.IndexedDBRequestManager):
(WebInspector.IndexedDBRequestManager.prototype._requestData.innerCallback):
(WebInspector.IndexedDBRequestManager.prototype._requestData):
(WebInspector.IndexedDBRequestManager.prototype.requestObjectStoreData):
(WebInspector.IndexedDBRequestManager.prototype._objectStoreDataLoaded):
(WebInspector.IndexedDBRequestManager.prototype.requestIndexData):
(WebInspector.IndexedDBRequestManager.prototype._indexDataLoaded):
(WebInspector.IndexedDBRequestManager.prototype._frameDetached):
(WebInspector.IndexedDBRequestManager.prototype._databaseRemoved):
(WebInspector.IndexedDBRequestManager.prototype._reset):
(WebInspector.IndexedDBRequestManager.DataRequest):
(WebInspector.IndexedDBDispatcher.prototype.databaseLoaded):
(WebInspector.IndexedDBDispatcher.prototype.objectStoreDataLoaded):
(WebInspector.IndexedDBDispatcher.prototype.indexDataLoaded):

LayoutTests:

* http/tests/inspector/indexeddb/database-data-expected.txt: Added.
* http/tests/inspector/indexeddb/database-data.html: Added.
* http/tests/inspector/indexeddb/database-names-expected.txt:
* http/tests/inspector/indexeddb/database-structure-expected.txt:
* http/tests/inspector/indexeddb/indexeddb-test.js:
(initialize_IndexedDBTest.InspectorTest.evaluateWithCallback):
(initialize_IndexedDBTest.InspectorTest.addIDBValue):
(initialize_IndexedDBTest):
(doWithReadWriteTransaction.step2.innerCommitCallback):
(doWithReadWriteTransaction.step2):
(doWithReadWriteTransaction):
(addIDBValue.doWithReadWriteTransaction.withTransactionCallback):
(addIDBValue):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@107801 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/inspector/indexeddb/database-data.html [new file with mode: 0644]
LayoutTests/http/tests/inspector/indexeddb/database-names-expected.txt
LayoutTests/http/tests/inspector/indexeddb/database-structure-expected.txt
LayoutTests/http/tests/inspector/indexeddb/indexeddb-test.js
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp
Source/WebCore/bindings/js/SerializedScriptValue.h
Source/WebCore/bindings/v8/SerializedScriptValue.cpp
Source/WebCore/bindings/v8/SerializedScriptValue.h
Source/WebCore/inspector/InjectedScript.cpp
Source/WebCore/inspector/InjectedScript.h
Source/WebCore/inspector/Inspector.json
Source/WebCore/inspector/InspectorController.cpp
Source/WebCore/inspector/InspectorIndexedDBAgent.cpp
Source/WebCore/inspector/InspectorIndexedDBAgent.h
Source/WebCore/inspector/front-end/IndexedDBModel.js

index 4875131..4d3dc74 100644 (file)
@@ -1,3 +1,24 @@
+2012-02-13  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: [InspectorIndexedDB] Pass data entries from object stores and indexes to front-end.
+        https://bugs.webkit.org/show_bug.cgi?id=78503
+
+        Reviewed by Yury Semikhatsky.
+
+        * http/tests/inspector/indexeddb/database-data-expected.txt: Added.
+        * http/tests/inspector/indexeddb/database-data.html: Added.
+        * http/tests/inspector/indexeddb/database-names-expected.txt:
+        * http/tests/inspector/indexeddb/database-structure-expected.txt:
+        * http/tests/inspector/indexeddb/indexeddb-test.js:
+        (initialize_IndexedDBTest.InspectorTest.evaluateWithCallback):
+        (initialize_IndexedDBTest.InspectorTest.addIDBValue):
+        (initialize_IndexedDBTest):
+        (doWithReadWriteTransaction.step2.innerCommitCallback):
+        (doWithReadWriteTransaction.step2):
+        (doWithReadWriteTransaction):
+        (addIDBValue.doWithReadWriteTransaction.withTransactionCallback):
+        (addIDBValue):
+
 2012-02-15  Kent Tamura  <tkent@chromium.org>
 
         Unreviewed, change the encoding of a test HTML.
diff --git a/LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt b/LayoutTests/http/tests/inspector/indexeddb/database-data-expected.txt
new file mode 100644 (file)
index 0000000..1587fad
--- /dev/null
@@ -0,0 +1,84 @@
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback5
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback6
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback7
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback8
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback9
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback10
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback11
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback12
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback13
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback14
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback15
+Tests that data is correctly loaded by IndexedDBModel from IndexedDB object store and index.
+
+Dumping values, fromIndex = false, skipCount = 0, pageSize = 3, idbKeyRange = null
+    Key = key_01, primaryKey = key_01, value = {"key":"key_01","value":"value_01"}
+    Key = key_02, primaryKey = key_02, value = {"key":"key_02","value":"value_02"}
+    Key = key_03, primaryKey = key_03, value = {"key":"key_03","value":"value_03"}
+    hasMore = true
+Dumping values, fromIndex = false, skipCount = 3, pageSize = 3, idbKeyRange = null
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    hasMore = true
+Dumping values, fromIndex = false, skipCount = 6, pageSize = 3, idbKeyRange = null
+    Key = key_07, primaryKey = key_07, value = {"key":"key_07","value":"value_07"}
+    Key = key_08, primaryKey = key_08, value = {"key":"key_08","value":"value_08"}
+    Key = key_09, primaryKey = key_09, value = {"key":"key_09","value":"value_09"}
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 7, pageSize = 3, idbKeyRange = null
+    Key = key_08, primaryKey = key_08, value = {"key":"key_08","value":"value_08"}
+    Key = key_09, primaryKey = key_09, value = {"key":"key_09","value":"value_09"}
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 9, pageSize = 3, idbKeyRange = null
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 10, pageSize = 3, idbKeyRange = null
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 0, pageSize = 4, idbKeyRange = {lower:"key_02",lowerOpen:true,upper:"key_07",upperOpen:false}
+    Key = key_03, primaryKey = key_03, value = {"key":"key_03","value":"value_03"}
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    hasMore = true
+Dumping values, fromIndex = false, skipCount = 0, pageSize = 5, idbKeyRange = {lower:"key_02",lowerOpen:true,upper:"key_07",upperOpen:false}
+    Key = key_03, primaryKey = key_03, value = {"key":"key_03","value":"value_03"}
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    Key = key_07, primaryKey = key_07, value = {"key":"key_07","value":"value_07"}
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 0, pageSize = 6, idbKeyRange = {lower:"key_02",lowerOpen:true,upper:"key_07",upperOpen:false}
+    Key = key_03, primaryKey = key_03, value = {"key":"key_03","value":"value_03"}
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    Key = key_07, primaryKey = key_07, value = {"key":"key_07","value":"value_07"}
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 1, pageSize = 4, idbKeyRange = {lower:"key_02",lowerOpen:true,upper:"key_07",upperOpen:false}
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    Key = key_07, primaryKey = key_07, value = {"key":"key_07","value":"value_07"}
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 1, pageSize = 5, idbKeyRange = {lower:"key_02",lowerOpen:true,upper:"key_07",upperOpen:false}
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    Key = key_07, primaryKey = key_07, value = {"key":"key_07","value":"value_07"}
+    hasMore = false
+Dumping values, fromIndex = false, skipCount = 1, pageSize = 6, idbKeyRange = {lower:"key_02",lowerOpen:true,upper:"key_07",upperOpen:false}
+    Key = key_04, primaryKey = key_04, value = {"key":"key_04","value":"value_04"}
+    Key = key_05, primaryKey = key_05, value = {"key":"key_05","value":"value_05"}
+    Key = key_06, primaryKey = key_06, value = {"key":"key_06","value":"value_06"}
+    Key = key_07, primaryKey = key_07, value = {"key":"key_07","value":"value_07"}
+    hasMore = false
+Dumping values, fromIndex = true, skipCount = 0, pageSize = 3, idbKeyRange = null
+    Key = value_01, primaryKey = key_01, value = {"key":"key_01","value":"value_01"}
+    Key = value_02, primaryKey = key_02, value = {"key":"key_02","value":"value_02"}
+    Key = value_03, primaryKey = key_03, value = {"key":"key_03","value":"value_03"}
+    hasMore = true
+
diff --git a/LayoutTests/http/tests/inspector/indexeddb/database-data.html b/LayoutTests/http/tests/inspector/indexeddb/database-data.html
new file mode 100644 (file)
index 0000000..d8537cf
--- /dev/null
@@ -0,0 +1,202 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="indexeddb-test.js"></script>
+<script>
+function test()
+{
+    var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
+    var indexedDBModel = new WebInspector.IndexedDBModel();
+    var mainFrameId = WebInspector.resourceTreeModel.mainFrame.id;
+    var databaseName = "testDatabase";
+    var objectStoreName = "testObjectStore";
+    var indexName = "testIndexName";
+
+    function addIDBValues(count, callback)
+    {
+        var i = 0;
+        addValues();
+
+        function addValues()
+        {
+            if (i == count) {
+                callback();
+                return;
+            }
+            ++i;
+            var id = i < 10 ? "0" + i : i;
+            var key = "key_" + id;
+            var value = "value_" + id;
+            InspectorTest.addIDBValue(mainFrameId, databaseName, objectStoreName, { key: key, value: value }, "", addValues);
+        }
+    }
+
+    function loadValuesAndDump(fromIndex, idbKeyRange, skipCount, pageSize, callback)
+    {
+        var idbKeyRangeString = idbKeyRange ? "{lower:\"" + idbKeyRange.lower + "\",lowerOpen:" + idbKeyRange.lowerOpen + ",upper:\"" + idbKeyRange.upper + "\",upperOpen:" + idbKeyRange.upperOpen + "}" : "null";
+        InspectorTest.addResult("Dumping values, fromIndex = " + fromIndex + ", skipCount = " + skipCount + ", pageSize = " + pageSize + ", idbKeyRange = " + idbKeyRangeString);
+        if (fromIndex)
+            indexedDBModel.loadIndexData(mainFrameId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, innerCallback);
+        else
+            indexedDBModel.loadObjectStoreData(mainFrameId, databaseName, objectStoreName, idbKeyRange, skipCount, pageSize, innerCallback);
+
+        function innerCallback(entries, hasMore)
+        {
+            var index = 0;
+            var entry;
+            dumpEntries();
+
+            function dumpEntries()
+            {
+                if (index === entries.length) {
+                    InspectorTest.addResult("    hasMore = " + hasMore);
+                    callback();
+                    return;
+                }
+                entry = entries[index];
+                entry.value.callFunctionJSON(dumpMe, dumped.bind(this));
+                ++index;
+            }
+
+            function dumpMe()
+            {
+                return "{\"key\":\"" + this.key + "\",\"value\":\"" + this.value + "\"}";
+            }
+
+            function dumped(value)
+            {
+                InspectorTest.addResult("    Key = " + entry.key + ", primaryKey = " + entry.primaryKey + ", value = " + value);
+                dumpEntries();
+            }
+        }
+    }
+
+    InspectorTest.addSniffer(WebInspector.IndexedDBDispatcher.prototype, "databaseNamesLoaded", fillDatabase, false);
+
+    function fillDatabase()
+    {
+        InspectorTest.createDatabase(mainFrameId, databaseName, step2);
+
+        function step2()
+        {
+            InspectorTest.createObjectStore(mainFrameId, databaseName, objectStoreName, "key", true, step3);
+        }
+
+        function step3()
+        {
+            InspectorTest.createObjectStoreIndex(mainFrameId, databaseName, objectStoreName, indexName, "value", false, true, step4);
+        }
+
+        function step4()
+        {
+            addIDBValues(9, refreshDatabase);
+        }
+    }
+
+    function refreshDatabase()
+    {
+        InspectorTest.addSniffer(WebInspector.IndexedDBDispatcher.prototype, "databaseLoaded", runObjectStoreTests, false);
+        indexedDBModel.refreshDatabase(mainFrameId, databaseName);
+    }
+
+    function runObjectStoreTests()
+    {
+        loadValuesAndDump(false, null, 0, 3, step2)
+
+        function step2()
+        {
+            loadValuesAndDump(false, null, 3, 3, step3)
+        }
+
+        function step3()
+        {
+            loadValuesAndDump(false, null, 6, 3, step4);
+        }
+
+        function step4()
+        {
+            loadValuesAndDump(false, null, 7, 3, step5);
+        }
+
+        function step5()
+        {
+            loadValuesAndDump(false, null, 9, 3, step6);
+        }
+
+        function step6()
+        {
+            loadValuesAndDump(false, null, 10, 3, step7);
+        }
+
+        function step7()
+        {
+            loadValuesAndDump(false, IDBKeyRange.bound("key_02", "key_07", true, false), 0, 4, step8);
+        }
+
+        function step8()
+        {
+            loadValuesAndDump(false, IDBKeyRange.bound("key_02", "key_07", true, false), 0, 5, step9);
+        }
+
+        function step9()
+        {
+            loadValuesAndDump(false, IDBKeyRange.bound("key_02", "key_07", true, false), 0, 6, step10);
+        }
+
+        function step10()
+        {
+            loadValuesAndDump(false, IDBKeyRange.bound("key_02", "key_07", true, false), 1, 4, step11);
+        }
+
+        function step11()
+        {
+            loadValuesAndDump(false, IDBKeyRange.bound("key_02", "key_07", true, false), 1, 5, step12);
+        }
+
+        function step12()
+        {
+            loadValuesAndDump(false, IDBKeyRange.bound("key_02", "key_07", true, false), 1, 6, step13);
+        }
+
+        function step13()
+        {
+            runIndexTests();
+        }
+    }
+
+    function runIndexTests()
+    {
+        loadValuesAndDump(true, null, 0, 3, step2)
+
+        function step2()
+        {
+            clearDatabase();
+        }
+    }
+
+    function clearDatabase()
+    {
+        InspectorTest.deleteObjectStoreIndex(mainFrameId, databaseName, objectStoreName, indexName, step2);
+
+        function step2()
+        {
+            InspectorTest.deleteObjectStore(mainFrameId, databaseName, objectStoreName, step3);
+        }
+
+        function step3()
+        {
+            InspectorTest.deleteDatabase(mainFrameId, databaseName, step4);
+        }
+
+        function step4()
+        {
+            InspectorTest.completeTest();
+        }
+    }
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests that data is correctly loaded by IndexedDBModel from IndexedDB object store and index.</p>
+</body>
+</html>
index 0fb344d..ecf8027 100644 (file)
@@ -1,7 +1,7 @@
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback4
 Tests that database names are correctly loaded and saved in IndexedDBModel.
 
 Dumping database names:
index 0285ac4..f86ba0f 100644 (file)
@@ -1,13 +1,13 @@
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback1
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback2
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback3
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback4
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback5
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback6
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback7
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback8
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback9
-CONSOLE MESSAGE: line 77: InspectorTest.IndexedDB_callback10
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback1
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback2
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback3
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback4
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback5
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback6
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback7
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback8
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback9
+CONSOLE MESSAGE: line 79: InspectorTest.IndexedDB_callback10
 Tests that database names are correctly loaded and saved in IndexedDBModel.
 
 Dumping database:
index 0285a0e..60602ef 100644 (file)
@@ -11,12 +11,8 @@ InspectorTest.evaluateWithCallback = function(frameId, methodName, parameters, c
     var callbackId = ++lastCallbackId;
     callbacks[callbackId] = callback;
     var parametersString = "\"" + callbackIdPrefix + callbackId + "\"";
-    for (var i = 0; i < parameters.length; ++i) {
-        if (typeof(parameters[i]) === "string")
-            parametersString += ", \"" + parameters[i] + "\"";
-        else if ((typeof(parameters[i]) === "number") || (typeof(parameters[i]) === "boolean"))
-            parametersString += ", " + parameters[i];
-    }
+    for (var i = 0; i < parameters.length; ++i)
+        parametersString += ", " + JSON.stringify(parameters[i]);
 
     var requestString = methodName + "(" + parametersString + ")";
     InspectorTest.evaluateInPage(requestString);
@@ -68,9 +64,15 @@ InspectorTest.deleteObjectStoreIndex = function(frameId, databaseName, objectSto
     InspectorTest.evaluateWithCallback(frameId, "deleteObjectStoreIndex", [databaseName, objectStoreName, objectStoreIndexName], callback)
 };
 
+InspectorTest.addIDBValue = function(frameId, databaseName, objectStoreName, value, key, callback)
+{
+    InspectorTest.evaluateWithCallback(frameId, "addIDBValue", [databaseName, objectStoreName, value, key], callback)
+};
+
 };
 
 var indexedDB = window.indexeddb || window.webkitIndexedDB;
+var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
 
 function dispatchCallback(callbackId)
 {
@@ -116,6 +118,24 @@ function doWithVersionTransaction(databaseName, callback, commitCallback)
     }
 }
 
+function doWithReadWriteTransaction(databaseName, objectStoreName, callback, commitCallback)
+{
+    doWithDatabase(databaseName, step2);
+
+    function step2(db)
+    {
+        var transaction = db.transaction([objectStoreName], IDBTransaction.READ_WRITE);
+        var objectStore = transaction.objectStore(objectStoreName);
+        callback(objectStore, innerCommitCallback);
+
+        function innerCommitCallback()
+        {
+            db.close();
+            commitCallback();
+        }
+    }
+}
+
 function createDatabase(callbackId, databaseName)
 {
     var request = indexedDB.open(databaseName, 0);
@@ -178,3 +198,19 @@ function deleteObjectStoreIndex(callbackId, databaseName, objectStoreName, objec
     }
 }
 
+function addIDBValue(callbackId, databaseName, objectStoreName, value, key)
+{
+    doWithReadWriteTransaction(databaseName, objectStoreName, withTransactionCallback, dispatchCallback.bind(this, callbackId))
+
+    function withTransactionCallback(objectStore, commitCallback)
+    {
+        var request;
+        if (key)
+            request = objectStore.add(value, key);
+        else
+            request = objectStore.add(value);
+        request.onerror = onIndexedDBError;
+        request.onsuccess = commitCallback;
+    }
+}
+
index fb5c8b0..401f064 100644 (file)
@@ -1,3 +1,65 @@
+2012-02-13  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: [InspectorIndexedDB] Pass data entries from object stores and indexes to front-end.
+        https://bugs.webkit.org/show_bug.cgi?id=78503
+
+        Reviewed by Yury Semikhatsky.
+
+        Test: http/tests/inspector/indexeddb/database-data.html
+
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::SerializedScriptValue::deserializeForInspector):
+        (WebCore):
+        * bindings/js/SerializedScriptValue.h:
+        (SerializedScriptValue):
+        * bindings/v8/SerializedScriptValue.cpp:
+        (WebCore::SerializedScriptValue::deserializeForInspector):
+        (WebCore):
+        * bindings/v8/SerializedScriptValue.h:
+        (SerializedScriptValue):
+        * inspector/InjectedScript.cpp:
+        (WebCore::InjectedScript::wrapObject):
+        (WebCore::InjectedScript::wrapSerializedObject):
+        (WebCore):
+        (WebCore::InjectedScript::canAccessInspectedWindow):
+        * inspector/InjectedScript.h:
+        (InjectedScript):
+        * inspector/Inspector.json:
+        * inspector/InspectorController.cpp:
+        (WebCore::InspectorController::InspectorController):
+        * inspector/InspectorIndexedDBAgent.cpp:
+        (WebCore):
+        (WebCore::InspectorIndexedDBAgent::InspectorIndexedDBAgent):
+        (WebCore::assertFrame):
+        (WebCore::assertDocument):
+        (WebCore::InspectorIndexedDBAgent::requestData):
+        * inspector/InspectorIndexedDBAgent.h:
+        (WebCore):
+        (WebCore::InspectorIndexedDBAgent::create):
+        (InspectorIndexedDBAgent):
+        * inspector/front-end/IndexedDBModel.js:
+        (WebInspector.IndexedDBModel.idbKeyFromKey):
+        (WebInspector.IndexedDBModel.keyFromIDBKey):
+        (WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange):
+        (WebInspector.IndexedDBModel.prototype._loadDatabase):
+        (WebInspector.IndexedDBModel.prototype.loadObjectStoreData):
+        (WebInspector.IndexedDBModel.prototype.loadIndexData):
+        (WebInspector.IndexedDBModel.Entry):
+        (WebInspector.IndexedDBRequestManager):
+        (WebInspector.IndexedDBRequestManager.prototype._requestData.innerCallback):
+        (WebInspector.IndexedDBRequestManager.prototype._requestData):
+        (WebInspector.IndexedDBRequestManager.prototype.requestObjectStoreData):
+        (WebInspector.IndexedDBRequestManager.prototype._objectStoreDataLoaded):
+        (WebInspector.IndexedDBRequestManager.prototype.requestIndexData):
+        (WebInspector.IndexedDBRequestManager.prototype._indexDataLoaded):
+        (WebInspector.IndexedDBRequestManager.prototype._frameDetached):
+        (WebInspector.IndexedDBRequestManager.prototype._databaseRemoved):
+        (WebInspector.IndexedDBRequestManager.prototype._reset):
+        (WebInspector.IndexedDBRequestManager.DataRequest):
+        (WebInspector.IndexedDBDispatcher.prototype.databaseLoaded):
+        (WebInspector.IndexedDBDispatcher.prototype.objectStoreDataLoaded):
+        (WebInspector.IndexedDBDispatcher.prototype.indexDataLoaded):
+
 2012-02-15  Hajime Morrita  <morrita@chromium.org>
 
         REGRESSION(r107518): DeviceOrientationController doesn't remove registered DOMWindows
index 3ac6ecf..5f58e14 100644 (file)
@@ -38,6 +38,7 @@
 #include "JSImageData.h"
 #include "JSMessagePort.h"
 #include "JSNavigator.h"
+#include "ScriptValue.h"
 #include "SharedBuffer.h"
 #include <limits>
 #include <JavaScriptCore/APICast.h>
@@ -1452,6 +1453,14 @@ JSValue SerializedScriptValue::deserialize(ExecState* exec, JSGlobalObject* glob
     return result.first;
 }
 
+#if ENABLE(INSPECTOR)
+ScriptValue SerializedScriptValue::deserializeForInspector(ScriptState* scriptState)
+{
+    JSValue value = deserialize(scriptState, scriptState->lexicalGlobalObject(), 0);
+    return ScriptValue(scriptState->globalData(), value);
+}
+#endif
+
 JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception, MessagePortArray* messagePorts)
 {
     ExecState* exec = toJS(destinationContext);
index caed647..75ce694 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef SerializedScriptValue_h
 #define SerializedScriptValue_h
 
+#include "ScriptState.h"
 #include <heap/Strong.h>
 #include <runtime/JSValue.h>
 #include <wtf/Forward.h>
@@ -52,6 +53,7 @@ enum SerializationReturnCode {
     
 enum SerializationErrorMode { NonThrowing, Throwing };
 
+class ScriptValue;
 class SharedBuffer;
 
 class SerializedScriptValue : public RefCounted<SerializedScriptValue> {
@@ -77,6 +79,10 @@ public:
     JSValueRef deserialize(JSContextRef, JSValueRef* exception, MessagePortArray*);
     JSValueRef deserialize(JSContextRef, JSValueRef* exception);
 
+#if ENABLE(INSPECTOR)
+    ScriptValue deserializeForInspector(ScriptState*);
+#endif
+
     const Vector<uint8_t>& data() { return m_data; }
 
     ~SerializedScriptValue();
index 9181b73..3072cb8 100644 (file)
@@ -2258,4 +2258,14 @@ v8::Handle<v8::Value> SerializedScriptValue::deserialize(MessagePortArray* messa
     return deserializer.deserialize();
 }
 
+#if ENABLE(INSPECTOR)
+ScriptValue SerializedScriptValue::deserializeForInspector(ScriptState* scriptState)
+{
+    v8::HandleScope handleScope;
+    v8::Context::Scope contextScope(scriptState->context());
+
+    return ScriptValue(deserialize());
+}
+#endif
+
 } // namespace WebCore
index d3a668b..68b1954 100644 (file)
@@ -76,6 +76,10 @@ public:
     // case of failure.
     v8::Handle<v8::Value> deserialize(MessagePortArray* = 0);
 
+#if ENABLE(INSPECTOR)
+    ScriptValue deserializeForInspector(ScriptState*);
+#endif
+
 private:
     enum StringDataMode {
         StringValue,
index 6d09b29..de6bbb7 100644 (file)
@@ -154,7 +154,7 @@ PassRefPtr<InspectorArray> InjectedScript::wrapCallFrames(const ScriptValue& cal
 }
 #endif
 
-PassRefPtr<InspectorObject> InjectedScript::wrapObject(ScriptValue value, const String& groupName)
+PassRefPtr<InspectorObject> InjectedScript::wrapObject(ScriptValue value, const String& groupName) const
 {
     ASSERT(!hasNoValue());
     ScriptFunctionCall wrapFunction(m_injectedScriptObject, "wrapObject");
@@ -176,6 +176,12 @@ PassRefPtr<InspectorObject> InjectedScript::wrapNode(Node* node, const String& g
     return wrapObject(nodeAsScriptValue(node), groupName);
 }
 
+PassRefPtr<InspectorObject> InjectedScript::wrapSerializedObject(SerializedScriptValue* serializedScriptValue, const String& groupName) const
+{
+    ScriptValue scriptValue = serializedScriptValue->deserializeForInspector(m_injectedScriptObject.scriptState());
+    return scriptValue.hasNoValue() ? 0 : wrapObject(scriptValue, groupName);
+}
+
 void InjectedScript::inspectNode(Node* node)
 {
     ASSERT(!hasNoValue());
@@ -193,7 +199,7 @@ void InjectedScript::releaseObjectGroup(const String& objectGroup)
     releaseFunction.call();
 }
 
-bool InjectedScript::canAccessInspectedWindow()
+bool InjectedScript::canAccessInspectedWindow() const
 {
     return m_inspectedStateAccessCheck(m_injectedScriptObject.scriptState());
 }
index f2b7ac1..a4ad096 100644 (file)
@@ -87,8 +87,9 @@ public:
     PassRefPtr<InspectorArray> wrapCallFrames(const ScriptValue&);
 #endif
 
-    PassRefPtr<InspectorObject> wrapObject(ScriptValue, const String& groupName);
+    PassRefPtr<InspectorObject> wrapObject(ScriptValue, const String& groupName) const;
     PassRefPtr<InspectorObject> wrapNode(Node*, const String& groupName);
+    PassRefPtr<InspectorObject> wrapSerializedObject(SerializedScriptValue*, const String& groupName) const;
     void inspectNode(Node*);
     void releaseObjectGroup(const String&);
     ScriptState* scriptState() const { return m_injectedScriptObject.scriptState(); }
@@ -98,7 +99,7 @@ private:
     typedef bool (*InspectedStateAccessCheck)(ScriptState*);
     InjectedScript(ScriptObject, InspectedStateAccessCheck);
 
-    bool canAccessInspectedWindow();
+    bool canAccessInspectedWindow() const;
     void makeCall(ScriptFunctionCall&, RefPtr<InspectorValue>* result);
     void makeEvalCall(ErrorString*, ScriptFunctionCall&, RefPtr<InspectorObject>* result, bool* wasThrown);
     ScriptValue nodeAsScriptValue(Node*);
index 4a464aa..392e555 100644 (file)
                     { "name": "unique", "type": "boolean", "description": "If true, index is unique." },
                     { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." }
                 ]
+            },
+            {
+                "id": "Key",
+                "type": "object",
+                "description": "Key.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["number", "string", "date", "array"], "description": "Key type." },
+                    { "name": "number", "type": "number", "optional": true, "description": "Number value." },
+                    { "name": "string", "type": "string", "optional": true, "description": "String value." },
+                    { "name": "date", "type": "number", "optional": true, "description": "Date value." },
+                    { "name": "array", "type": "array", "optional": true, "items": { "$ref": "Key" }, "description": "Array value." }
+                ]
+            },
+            {
+                "id": "KeyRange",
+                "type": "object",
+                "description": "Key range.",
+                "properties": [
+                    { "name": "lower", "$ref": "Key", "optional": true, "description": "Lower bound." },
+                    { "name": "upper", "$ref": "Key", "optional": true, "description": "Upper bound." },
+                    { "name": "lowerOpen", "type": "boolean", "description": "If true lower bound is open." },
+                    { "name": "upperOpen", "type": "boolean", "description": "If true upper bound is open." }
+                ]
+            },
+            {
+                "id": "DataEntry",
+                "type": "object",
+                "description": "Key.",
+                "properties": [
+                    { "name": "key", "$ref": "Key", "description": "Key." },
+                    { "name": "primaryKey", "$ref": "Key", "description": "Primary key." },
+                    { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Value." }
+                ]
             }
         ],
         "commands": [
                 "name": "requestDatabase",
                 "parameters": [
                     { "name": "requestId", "type": "integer", "description": "Request id." },
-                    { "name": "frameId", "$ref": "Network.FrameId" },
-                    { "name": "databaseName", "type": "string" }
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id." },
+                    { "name": "databaseName", "type": "string", "description": "Database name." }
                 ],
                 "description": "Requests database with given name in given frame."
+            },
+            {
+                "name": "requestData",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request id." },
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id." },
+                    { "name": "databaseName", "type": "string", "description": "Database name." },
+                    { "name": "objectStoreName", "type": "string", "description": "Object store name." },
+                    { "name": "indexName", "type": "string", "description": "Index name, empty string for object store data requests." },
+                    { "name": "skipCount", "type": "integer", "description": "Number of records to skip." },
+                    { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." },
+                    { "name": "keyRange", "$ref": "KeyRange", "optional": true, "description": "Key range." }
+                ],
+                "description": "Requests data from object store or index."
             }
         ],
         "events": [
                     { "name": "requestId", "type": "integer", "description": "Request id." },
                     { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." }
                 ]
+            },
+            {
+                "name": "objectStoreDataLoaded",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request id." },
+                    { "name": "objectStoreDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." },
+                    { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." }
+                ]
+            },
+            {
+                "name": "indexDataLoaded",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request id." },
+                    { "name": "indexDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of index data entries." },
+                    { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." }
+                ]
             }
         ]
     },
index ec50bc5..339eeff 100644 (file)
@@ -101,7 +101,7 @@ InspectorController::InspectorController(Page* page, InspectorClient* inspectorC
 #endif
 
 #if ENABLE(INDEXED_DATABASE)
-    m_agents.append(InspectorIndexedDBAgent::create(m_instrumentingAgents.get(), m_state.get(), pageAgent));
+    m_agents.append(InspectorIndexedDBAgent::create(m_instrumentingAgents.get(), m_state.get(), m_injectedScriptManager.get(), pageAgent));
 #endif
 
 #if ENABLE(FILE_SYSTEM)
index b005259..dcd43b3 100644 (file)
 #include "Frame.h"
 #include "GroupSettings.h"
 #include "IDBCallbacks.h"
+#include "IDBCursor.h"
 #include "IDBDatabaseBackendInterface.h"
 #include "IDBFactoryBackendInterface.h"
 #include "IDBIndexBackendInterface.h"
+#include "IDBKey.h"
+#include "IDBKeyRange.h"
 #include "IDBObjectStoreBackendInterface.h"
+#include "IDBPendingTransactionMonitor.h"
 #include "IDBTransaction.h"
 #include "IDBTransactionBackendInterface.h"
+#include "InjectedScript.h"
 #include "InspectorFrontend.h"
 #include "InspectorPageAgent.h"
 #include "InspectorState.h"
 
 #include <wtf/Vector.h>
 
+using WebCore::TypeBuilder::Array;
 using WebCore::TypeBuilder::IndexedDB::SecurityOriginWithDatabaseNames;
 using WebCore::TypeBuilder::IndexedDB::DatabaseWithObjectStores;
+using WebCore::TypeBuilder::IndexedDB::DataEntry;
+using WebCore::TypeBuilder::IndexedDB::Key;
+using WebCore::TypeBuilder::IndexedDB::KeyRange;
 using WebCore::TypeBuilder::IndexedDB::ObjectStore;
 using WebCore::TypeBuilder::IndexedDB::ObjectStoreIndex;
 
@@ -102,6 +111,21 @@ public:
     virtual void onBlocked() { }
 };
 
+class InspectorIDBTransactionCallback : public IDBTransactionCallbacks {
+public:
+    static PassRefPtr<InspectorIDBTransactionCallback> create()
+    {
+        return adoptRef(new InspectorIDBTransactionCallback());
+    }
+
+    virtual ~InspectorIDBTransactionCallback() { }
+
+    virtual void onAbort() { }
+    virtual void onComplete() { }
+private:
+    InspectorIDBTransactionCallback() { }
+};
+
 class GetDatabaseNamesCallback : public InspectorIDBCallback {
 public:
     static PassRefPtr<GetDatabaseNamesCallback> create(InspectorIndexedDBAgent::FrontendProvider* frontendProvider, int requestId, const String& securityOrigin)
@@ -262,10 +286,282 @@ private:
     int m_requestId;
 };
 
+static PassRefPtr<IDBKey> idbKeyFromInspectorObject(InspectorObject* key)
+{
+    RefPtr<IDBKey> idbKey;
+
+    String type;
+    if (!key->getString("type", &type))
+        return 0;
+
+    DEFINE_STATIC_LOCAL(String, number, ("number"));
+    DEFINE_STATIC_LOCAL(String, string, ("string"));
+    DEFINE_STATIC_LOCAL(String, date, ("date"));
+    DEFINE_STATIC_LOCAL(String, array, ("array"));
+
+    if (type == number) {
+        double number;
+        if (!key->getNumber("number", &number))
+            return 0;
+        idbKey = IDBKey::createNumber(number);
+    } else if (type == string) {
+        String string;
+        if (!key->getString("string", &string))
+            return 0;
+        idbKey = IDBKey::createString(string);
+    } else if (type == date) {
+        double date;
+        if (!key->getNumber("date", &date))
+            return 0;
+        idbKey = IDBKey::createDate(date);
+    } else if (type == array) {
+        IDBKey::KeyArray keyArray;
+        RefPtr<InspectorArray> array = key->getArray("array");
+        for (size_t i = 0; i < array->length(); ++i) {
+            RefPtr<InspectorValue> value = array->get(i);
+            RefPtr<InspectorObject> object;
+            if (!value->asObject(&object))
+                return 0;
+            keyArray.append(idbKeyFromInspectorObject(object.get()));
+        }
+        idbKey = IDBKey::createArray(keyArray);
+    } else
+        return 0;
+
+    return idbKey.release();
+}
+
+static PassRefPtr<IDBKeyRange> idbKeyRangeFromKeyRange(InspectorObject* keyRange)
+{
+    RefPtr<InspectorObject> lower = keyRange->getObject("lower");
+    RefPtr<IDBKey> idbLower = lower ? idbKeyFromInspectorObject(lower.get()) : 0;
+    if (lower && !idbLower)
+        return 0;
+
+    RefPtr<InspectorObject> upper = keyRange->getObject("upper");
+    RefPtr<IDBKey> idbUpper = upper ? idbKeyFromInspectorObject(upper.get()) : 0;
+    if (upper && !idbUpper)
+        return 0;
+
+    bool lowerOpen;
+    if (!keyRange->getBoolean("lowerOpen", &lowerOpen))
+        return 0;
+    IDBKeyRange::LowerBoundType lowerBoundType = lowerOpen ? IDBKeyRange::LowerBoundOpen : IDBKeyRange::LowerBoundClosed;
+
+    bool upperOpen;
+    if (!keyRange->getBoolean("upperOpen", &upperOpen))
+        return 0;
+    IDBKeyRange::UpperBoundType upperBoundType = upperOpen ? IDBKeyRange::UpperBoundOpen : IDBKeyRange::UpperBoundClosed;
+
+    RefPtr<IDBKeyRange> idbKeyRange = IDBKeyRange::create(idbLower, idbUpper, lowerBoundType, upperBoundType);
+    return idbKeyRange.release();
+}
+
+static PassRefPtr<Key> keyFromIDBKey(IDBKey* idbKey)
+{
+    if (!idbKey || !idbKey->valid())
+        return 0;
+
+    RefPtr<Key> key;
+    switch (idbKey->type()) {
+    case IDBKey::InvalidType:
+    case IDBKey::MinType:
+        return 0;
+    case IDBKey::NumberType: {
+        RefPtr<Key> tmpKey = Key::create().setType(Key::Type::Number);
+        key = tmpKey;
+        key->setNumber(idbKey->number());
+        break;
+    }
+    case IDBKey::StringType: {
+        RefPtr<Key> tmpKey = Key::create().setType(Key::Type::String);
+        key = tmpKey;
+        key->setString(idbKey->string());
+        break;
+    }
+    case IDBKey::DateType: {
+        RefPtr<Key> tmpKey = Key::create().setType(Key::Type::Date);
+        key = tmpKey;
+        key->setDate(idbKey->date());
+        break;
+    }
+    case IDBKey::ArrayType: {
+        RefPtr<Key> tmpKey = Key::create().setType(Key::Type::Array);
+        key = tmpKey;
+        RefPtr<InspectorArray> array = InspectorArray::create();
+        IDBKey::KeyArray keyArray = idbKey->array();
+        for (size_t i = 0; i < keyArray.size(); ++i)
+            array->pushObject(keyFromIDBKey(keyArray[i].get()));
+        key->setArray(array);
+        break;
+    }
+    }
+    return key.release();
+}
+
+class OpenCursorCallback : public InspectorIDBCallback {
+public:
+    enum CursorType {
+        ObjectStoreDataCursor,
+        IndexDataCursor
+    };
+
+    static PassRefPtr<OpenCursorCallback> create(PassRefPtr<InspectorIndexedDBAgent::FrontendProvider> frontendProvider, InjectedScript injectedScript, PassRefPtr<IDBTransactionBackendInterface> idbTransaction, CursorType cursorType, int requestId, int skipCount, unsigned pageSize)
+    {
+        return adoptRef(new OpenCursorCallback(frontendProvider, injectedScript, idbTransaction, cursorType, requestId, skipCount, pageSize));
+    }
+
+    virtual ~OpenCursorCallback() { }
+
+    virtual void onSuccess(PassRefPtr<SerializedScriptValue>)
+    {
+        end(false);
+    }
+
+    virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface> idbCursor)
+    {
+        m_idbCursor = idbCursor;
+        onSuccessWithContinuation();
+    }
+
+    virtual void onSuccessWithContinuation()
+    {
+        if (m_skipCount) {
+            --m_skipCount;
+            next();
+            return;
+        }
+
+        if (m_result->length() == m_pageSize) {
+            end(true);
+            return;
+        }
+
+        RefPtr<IDBKey> key = m_idbCursor->key();
+        RefPtr<IDBKey> primaryKey = m_idbCursor->primaryKey();
+        RefPtr<SerializedScriptValue> value = m_idbCursor->value();
+        RefPtr<InspectorObject> wrappedValue = m_injectedScript.wrapSerializedObject(value.get(), String());
+        RefPtr<DataEntry> dataEntry = DataEntry::create()
+            .setKey(keyFromIDBKey(key.get()))
+            .setPrimaryKey(keyFromIDBKey(primaryKey.get()))
+            .setValue(wrappedValue);
+        m_result->addItem(dataEntry);
+
+        next();
+    }
+
+    void next()
+    {
+        m_idbTransaction->didCompleteTaskEvents();
+
+        ExceptionCode ec = 0;
+        m_idbCursor->continueFunction(0, this, ec);
+    }
+
+    void end(bool hasMore)
+    {
+        if (!m_frontendProvider->frontend())
+            return;
+
+        m_idbTransaction->didCompleteTaskEvents();
+
+        switch (m_cursorType) {
+        case ObjectStoreDataCursor:
+            m_frontendProvider->frontend()->objectStoreDataLoaded(m_requestId, m_result.release(), hasMore);
+            break;
+        case IndexDataCursor:
+            m_frontendProvider->frontend()->indexDataLoaded(m_requestId, m_result.release(), hasMore);
+            break;
+        }
+    }
+
+private:
+    OpenCursorCallback(PassRefPtr<InspectorIndexedDBAgent::FrontendProvider> frontendProvider, InjectedScript injectedScript, PassRefPtr<IDBTransactionBackendInterface> idbTransaction, CursorType cursorType, int requestId, int skipCount, unsigned pageSize)
+        : m_frontendProvider(frontendProvider)
+        , m_injectedScript(injectedScript)
+        , m_idbTransaction(idbTransaction)
+        , m_cursorType(cursorType)
+        , m_requestId(requestId)
+        , m_skipCount(skipCount)
+        , m_pageSize(pageSize)
+    {
+        m_result = Array<DataEntry>::create();
+        m_idbTransaction->setCallbacks(InspectorIDBTransactionCallback::create().get());
+    }
+    RefPtr<InspectorIndexedDBAgent::FrontendProvider> m_frontendProvider;
+    InjectedScript m_injectedScript;
+    RefPtr<IDBTransactionBackendInterface> m_idbTransaction;
+    CursorType m_cursorType;
+    int m_requestId;
+    int m_skipCount;
+    unsigned m_pageSize;
+    RefPtr<Array<DataEntry> > m_result;
+    RefPtr<IDBCursorBackendInterface> m_idbCursor;
+};
+
+class DataLoaderCallback : public ExecutableWithDatabase {
+public:
+    static PassRefPtr<DataLoaderCallback> create(PassRefPtr<InspectorIndexedDBAgent::FrontendProvider> frontendProvider, int requestId, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, PassRefPtr<IDBKeyRange> idbKeyRange, int skipCount, unsigned pageSize)
+    {
+        return adoptRef(new DataLoaderCallback(frontendProvider, requestId, injectedScript, objectStoreName, indexName, idbKeyRange, skipCount, pageSize));
+    }
+
+    virtual ~DataLoaderCallback() { }
+
+    virtual void execute(PassRefPtr<IDBDatabaseBackendInterface> idbDatabase)
+    {
+        if (!m_frontendProvider->frontend())
+            return;
+
+        RefPtr<IDBTransactionBackendInterface> idbTransaction = transactionForDatabase(idbDatabase.get(), m_objectStoreName);
+        if (!idbTransaction)
+            return;
+        RefPtr<IDBObjectStoreBackendInterface> idbObjectStore = objectStoreForTransaction(idbTransaction.get(), m_objectStoreName);
+        if (!idbObjectStore)
+            return;
+
+        if (!m_indexName.isEmpty()) {
+            RefPtr<IDBIndexBackendInterface> idbIndex = indexForObjectStore(idbObjectStore.get(), m_indexName);
+            if (!idbIndex)
+                return;
+
+            RefPtr<OpenCursorCallback> openCursorCallback = OpenCursorCallback::create(m_frontendProvider, m_injectedScript, idbTransaction.get(), OpenCursorCallback::IndexDataCursor, m_requestId, m_skipCount, m_pageSize);
+
+            ExceptionCode ec = 0;
+            idbIndex->openCursor(m_idbKeyRange, IDBCursor::NEXT, openCursorCallback, idbTransaction.get(), ec);
+        } else {
+            RefPtr<OpenCursorCallback> openCursorCallback = OpenCursorCallback::create(m_frontendProvider, m_injectedScript, idbTransaction.get(), OpenCursorCallback::ObjectStoreDataCursor, m_requestId, m_skipCount, m_pageSize);
+
+            ExceptionCode ec = 0;
+            idbObjectStore->openCursor(m_idbKeyRange, IDBCursor::NEXT, openCursorCallback, idbTransaction.get(), ec);
+        }
+    }
+
+private:
+    DataLoaderCallback(PassRefPtr<InspectorIndexedDBAgent::FrontendProvider> frontendProvider, int requestId, const InjectedScript& injectedScript, const String& objectStoreName, const String& indexName, PassRefPtr<IDBKeyRange> idbKeyRange, int skipCount, unsigned pageSize)
+        : m_frontendProvider(frontendProvider)
+        , m_requestId(requestId)
+        , m_injectedScript(injectedScript)
+        , m_objectStoreName(objectStoreName)
+        , m_indexName(indexName)
+        , m_idbKeyRange(idbKeyRange)
+        , m_skipCount(skipCount)
+        , m_pageSize(pageSize) { }
+    RefPtr<InspectorIndexedDBAgent::FrontendProvider> m_frontendProvider;
+    int m_requestId;
+    InjectedScript m_injectedScript;
+    String m_objectStoreName;
+    String m_indexName;
+    RefPtr<IDBKeyRange> m_idbKeyRange;
+    int m_skipCount;
+    unsigned m_pageSize;
+};
+
 } // namespace
 
-InspectorIndexedDBAgent::InspectorIndexedDBAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InspectorPageAgent* pageAgent)
+InspectorIndexedDBAgent::InspectorIndexedDBAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, InspectorPageAgent* pageAgent)
     : InspectorBaseAgent<InspectorIndexedDBAgent>("IndexedDB", instrumentingAgents, state)
+    , m_injectedScriptManager(injectedScriptManager)
     , m_pageAgent(pageAgent)
 {
 }
@@ -304,34 +600,54 @@ void InspectorIndexedDBAgent::disable(ErrorString*)
     m_state->setBoolean(IndexedDBAgentState::indexedDBAgentEnabled, false);
 }
 
-static Document* assertDocument(const String& frameId, InspectorPageAgent* pageAgent, ErrorString* error)
+static Frame* assertFrame(ErrorString* errorString, const String& frameId, InspectorPageAgent* pageAgent)
 {
     Frame* frame = pageAgent->frameForId(frameId);
+
+    if (!frame)
+        *errorString = "Frame not found";
+
+    return frame;
+}
+
+static Document* assertDocument(ErrorString* errorString, Frame* frame)
+{
     Document* document = frame ? frame->document() : 0;
 
     if (!document)
-        *error = "No document for given frame found";
+        *errorString = "No document for given frame found";
 
     return document;
 }
 
-static IDBFactoryBackendInterface* assertIDBFactory(Document* document, ErrorString* error)
+static Document* assertDocument(ErrorString* errorString, const String& frameId, InspectorPageAgent* pageAgent)
+{
+    Frame* frame = pageAgent->frameForId(frameId);
+    Document* document = frame ? frame->document() : 0;
+
+    if (!document)
+        *errorString = "No document for given frame found";
+
+    return document;
+}
+
+static IDBFactoryBackendInterface* assertIDBFactory(ErrorString* errorString, Document* document)
 {
     Page* page = document ? document->page() : 0;
     IDBFactoryBackendInterface* idbFactory = page ? page->group().idbFactory() : 0;
 
     if (!idbFactory)
-        *error = "No IndexedDB factory for given frame found";
+        *errorString = "No IndexedDB factory for given frame found";
 
     return idbFactory;
 }
 
-void InspectorIndexedDBAgent::requestDatabaseNamesForFrame(ErrorString* error, int requestId, const String& frameId)
+void InspectorIndexedDBAgent::requestDatabaseNamesForFrame(ErrorString* errorString, int requestId, const String& frameId)
 {
-    Document* document = assertDocument(frameId, m_pageAgent, error);
+    Document* document = assertDocument(errorString, frameId, m_pageAgent);
     if (!document)
         return;
-    IDBFactoryBackendInterface* idbFactory = assertIDBFactory(document, error);
+    IDBFactoryBackendInterface* idbFactory = assertIDBFactory(errorString, document);
     if (!idbFactory)
         return;
 
@@ -340,12 +656,12 @@ void InspectorIndexedDBAgent::requestDatabaseNamesForFrame(ErrorString* error, i
     idbFactory->getDatabaseNames(callback.get(), document->securityOrigin(), document->frame(), groupSettings->indexedDBDatabasePath());
 }
 
-void InspectorIndexedDBAgent::requestDatabase(ErrorString* error, int requestId, const String& frameId, const String& databaseName)
+void InspectorIndexedDBAgent::requestDatabase(ErrorString* errorString, int requestId, const String& frameId, const String& databaseName)
 {
-    Document* document = assertDocument(frameId, m_pageAgent, error);
+    Document* document = assertDocument(errorString, frameId, m_pageAgent);
     if (!document)
         return;
-    IDBFactoryBackendInterface* idbFactory = assertIDBFactory(document, error);
+    IDBFactoryBackendInterface* idbFactory = assertIDBFactory(errorString, document);
     if (!idbFactory)
         return;
 
@@ -353,6 +669,30 @@ void InspectorIndexedDBAgent::requestDatabase(ErrorString* error, int requestId,
     databaseLoaderCallback->start(idbFactory, document->securityOrigin(), document->frame(), databaseName);
 }
 
+void InspectorIndexedDBAgent::requestData(ErrorString* errorString, int requestId, const String& frameId, const String& databaseName, const String& objectStoreName, const String& indexName, int skipCount, int pageSize, const RefPtr<InspectorObject>* keyRange)
+{
+    Frame* frame = assertFrame(errorString, frameId, m_pageAgent);
+    if (!frame)
+        return;
+    Document* document = assertDocument(errorString, frame);
+    if (!document)
+        return;
+    IDBFactoryBackendInterface* idbFactory = assertIDBFactory(errorString, document);
+    if (!idbFactory)
+        return;
+
+    InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
+
+    RefPtr<IDBKeyRange> idbKeyRange = keyRange ? idbKeyRangeFromKeyRange(keyRange->get()) : 0;
+    if (keyRange && !idbKeyRange) {
+        *errorString = "Can not parse key range.";
+        return;
+    }
+
+    RefPtr<DataLoaderCallback> dataLoaderCallback = DataLoaderCallback::create(m_frontendProvider, requestId, injectedScript, objectStoreName, indexName, idbKeyRange, skipCount, pageSize);
+    dataLoaderCallback->start(idbFactory, document->securityOrigin(), document->frame(), databaseName);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INSPECTOR) && ENABLE(INDEXED_DATABASE)
index 1b46dd1..b19ed6e 100644 (file)
@@ -39,6 +39,7 @@
 
 namespace WebCore {
 
+class InjectedScriptManager;
 class InspectorPageAgent;
 
 typedef String ErrorString;
@@ -47,9 +48,9 @@ class InspectorIndexedDBAgent : public InspectorBaseAgent<InspectorIndexedDBAgen
 public:
     class FrontendProvider;
 
-    static PassOwnPtr<InspectorIndexedDBAgent> create(InstrumentingAgents* instrumentingAgents, InspectorState* state, InspectorPageAgent* pageAgent)
+    static PassOwnPtr<InspectorIndexedDBAgent> create(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, InspectorPageAgent* pageAgent)
     {
-        return adoptPtr(new InspectorIndexedDBAgent(instrumentingAgents, state, pageAgent));
+        return adoptPtr(new InspectorIndexedDBAgent(instrumentingAgents, state, injectedScriptManager, pageAgent));
     }
     ~InspectorIndexedDBAgent();
 
@@ -62,9 +63,11 @@ public:
     virtual void disable(ErrorString*);
     virtual void requestDatabaseNamesForFrame(ErrorString*, int requestId, const String& frameId);
     virtual void requestDatabase(ErrorString*, int requestId, const String& frameId, const String& databaseName);
+    virtual void requestData(ErrorString*, int requestId, const String& frameId, const String& databaseName, const String& objectStoreName, const String& indexName, int skipCount, int pageSize, const RefPtr<InspectorObject>* keyRange);
 private:
-    InspectorIndexedDBAgent(InstrumentingAgents*, InspectorState*, InspectorPageAgent*);
+    InspectorIndexedDBAgent(InstrumentingAgents*, InspectorState*, InjectedScriptManager*, InspectorPageAgent*);
 
+    InjectedScriptManager* m_injectedScriptManager;
     InspectorPageAgent* m_pageAgent;
     RefPtr<FrontendProvider> m_frontendProvider;
     bool m_enabled;
index 75716d2..27ea7e5 100644 (file)
 WebInspector.IndexedDBModel = function()
 {
     this._indexedDBRequestManager = new WebInspector.IndexedDBRequestManager();
-    
+
     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
-    
+
     this._frames = {};
     this._frameIdsBySecurityOrigin = {};
     this._databaseNamesBySecurityOrigin = {};
@@ -46,18 +46,96 @@ WebInspector.IndexedDBModel = function()
     this.refreshDatabaseNames();
 }
 
+WebInspector.IndexedDBModel.KeyTypes = {
+    NumberType:  "number",
+    StringType:  "string",
+    DateType:    "date",
+    ArrayType:   "array"
+};
+
+/**
+ * @param {IndexedDBAgent.Key} key
+ */
+WebInspector.IndexedDBModel.idbKeyFromKey = function(key)
+{
+    var idbKey;
+    switch (key.type) {
+    case WebInspector.IndexedDBModel.KeyTypes.NumberType:
+        idbKey = key.number;
+        break;
+    case WebInspector.IndexedDBModel.KeyTypes.StringType:
+        idbKey = key.string;
+        break;
+    case WebInspector.IndexedDBModel.KeyTypes.DateType:
+        idbKey = new Date(key.date);
+        break;
+    case WebInspector.IndexedDBModel.KeyTypes.ArrayType:
+        idbKey = [];
+        for (var i = 0; i < key.length; ++i)
+            idbKey.push(WebInspector.IndexedDBModel.idbKeyFromKey(key.array[i]));
+        break;
+    }
+    return idbKey;
+}
+
+WebInspector.IndexedDBModel.keyFromIDBKey = function(idbKey)
+{
+    if (typeof(idbKey) === "undefined" || idbKey === null)
+        return null;
+
+    var key = {};
+    switch (typeof(idbKey)) {
+    case "number":
+        key.number = idbKey;
+        key.type = WebInspector.IndexedDBModel.KeyTypes.NumberType;
+        break;
+    case "string":
+        key.string = idbKey;
+        key.type = WebInspector.IndexedDBModel.KeyTypes.StringType;
+        break;
+    case "object":
+        if (idbKey instanceof Date) {
+            key.date = idbKey.getTime();
+            key.type = WebInspector.IndexedDBModel.KeyTypes.DateType;
+        } else if (idbKey instanceof Array) {
+            key.array = [];
+            for (var i = 0; i < idbKey.length; ++i)
+                key.array.push(WebInspector.IndexedDBModel.keyFromIDBKey(idbKey[i]));
+            key.type = WebInspector.IndexedDBModel.KeyTypes.ArrayType;
+        }
+        break;
+    default:
+        return null;
+    }
+    return key;
+}
+
+WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange = function(idbKeyRange)
+{
+    var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
+    if (typeof(idbKeyRange) === "undefined" || idbKeyRange === null)
+        return null;
+
+    var keyRange = {};
+    keyRange.lower = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.lower);
+    keyRange.upper = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.upper);
+    keyRange.lowerOpen = idbKeyRange.lowerOpen;
+    keyRange.upperOpen = idbKeyRange.upperOpen;
+    return keyRange;
+}
+
 WebInspector.IndexedDBModel.prototype = {
     refreshDatabaseNames: function()
     {
         this._reset();
         this._framesNavigatedRecursively(WebInspector.resourceTreeModel.mainFrame);
     },
-    
+
     refreshDatabase: function(frameId, databaseName)
     {
         this._loadDatabase(frameId, databaseName);
     },
-    
+
     /**
      * @param {WebInspector.ResourceTreeFrame} resourceTreeFrame
      */
@@ -65,9 +143,9 @@ WebInspector.IndexedDBModel.prototype = {
     {
         this._processFrameNavigated(resourceTreeFrame);
         for (var i = 0; i < resourceTreeFrame.childFrames.length; ++i)
-            this._framesNavigatedRecursively(resourceTreeFrame.childFrames[i]);            
+            this._framesNavigatedRecursively(resourceTreeFrame.childFrames[i]);
     },
-    
+
     /**
      * @param {WebInspector.Event} event
      */
@@ -76,7 +154,7 @@ WebInspector.IndexedDBModel.prototype = {
         var resourceTreeFrame = /** @type {WebInspector.ResourceTreeFrame} */ event.data;
         this._processFrameNavigated(resourceTreeFrame);
     },
-    
+
     /**
      * @param {WebInspector.Event} event
      */
@@ -263,6 +341,67 @@ WebInspector.IndexedDBModel.prototype = {
         }
 
         this._indexedDBRequestManager.requestDatabase(frameId, databaseName, callback.bind(this));
+    },
+
+    /**
+     * @param {string} frameId
+     * @param {string} databaseName
+     * @param {string} objectStoreName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
+     */
+    loadObjectStoreData: function(frameId, databaseName, objectStoreName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        /**
+         * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+         * @param {boolean} hasMore
+         */
+        function innerCallback(dataEntries, hasMore)
+        {
+            var entries = [];
+            for (var i = 0; i < dataEntries.length; ++i) {
+                var key = WebInspector.IndexedDBModel.idbKeyFromKey(dataEntries[i].key);
+                var primaryKey = WebInspector.IndexedDBModel.idbKeyFromKey(dataEntries[i].primaryKey);
+                var value = WebInspector.RemoteObject.fromPayload(dataEntries[i].value);
+                entries.push(new WebInspector.IndexedDBModel.Entry(key, primaryKey, value));
+            }
+            callback(entries, hasMore);
+        }
+
+        this._indexedDBRequestManager.requestObjectStoreData(frameId, databaseName, objectStoreName, idbKeyRange, skipCount, pageSize, innerCallback);
+    },
+
+    /**
+     * @param {string} frameId
+     * @param {string} databaseName
+     * @param {string} objectStoreName
+     * @param {string} indexName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
+     */
+    loadIndexData: function(frameId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        /**
+         * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+         * @param {boolean} hasMore
+         */
+        function innerCallback(dataEntries, hasMore)
+        {
+            var entries = [];
+            for (var i = 0; i < dataEntries.length; ++i) {
+                var key = WebInspector.IndexedDBModel.idbKeyFromKey(dataEntries[i].key);
+                var primaryKey = WebInspector.IndexedDBModel.idbKeyFromKey(dataEntries[i].primaryKey);
+                var value = WebInspector.RemoteObject.fromPayload(dataEntries[i].value);
+                entries.push(new WebInspector.IndexedDBModel.Entry(key, primaryKey, value));
+            }
+            callback(entries, hasMore);
+        }
+
+        this._indexedDBRequestManager.requestIndexData(frameId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, innerCallback.bind(this));
     }
 }
 
@@ -270,6 +409,19 @@ WebInspector.IndexedDBModel.prototype.__proto__ = WebInspector.Object.prototype;
 
 /**
  * @constructor
+ * @param {*} key
+ * @param {*} primaryKey
+ * @param {WebInspector.RemoteObject} value
+ */
+WebInspector.IndexedDBModel.Entry = function(key, primaryKey, value)
+{
+    this.key = key;
+    this.primaryKey = primaryKey;
+    this.value = value;
+}
+
+/**
+ * @constructor
  * @param {string} frameId
  * @param {string} securityOrigin
  */
@@ -323,6 +475,8 @@ WebInspector.IndexedDBRequestManager = function()
 {
     this._lastRequestId = 0;
     this._requestDatabaseNamesForFrameCallbacks = {};
+    this._requestDatabaseCallbacks = {};
+    this._requestDataCallbacks = {};
 
     IndexedDBAgent.enable();
     InspectorBackend.registerIndexedDBDispatcher(new WebInspector.IndexedDBDispatcher(this));
@@ -384,7 +538,7 @@ WebInspector.IndexedDBRequestManager.prototype = {
 
         IndexedDBAgent.requestDatabase(requestId, frameId, databaseName, innerCallback);
     },
-    
+
     /**
      * @param {number} requestId
      * @param {IndexedDBAgent.DatabaseWithObjectStores} databaseWithObjectStores
@@ -399,6 +553,91 @@ WebInspector.IndexedDBRequestManager.prototype = {
     },
 
     /**
+     * @param {string} frameId
+     * @param {string} databaseName
+     * @param {string} objectStoreName
+     * @param {string} indexName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<IndexedDBAgent.DataEntry>, boolean)} callback
+     */
+    _requestData: function(frameId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        var requestId = this._requestId();
+        var request = new WebInspector.IndexedDBRequestManager.DataRequest(frameId, databaseName, objectStoreName, indexName, callback);
+        this._requestDataCallbacks[requestId] = request;
+
+        function innerCallback(error)
+        {
+            if (error) {
+                console.error("IndexedDBAgent error: " + error);
+                return;
+            }
+        }
+
+        var keyRange = WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange);
+        IndexedDBAgent.requestData(requestId, frameId, databaseName, objectStoreName, indexName, skipCount, pageSize, keyRange ? keyRange : undefined, innerCallback);
+    },
+
+    /**
+     * @param {string} frameId
+     * @param {string} databaseName
+     * @param {string} objectStoreName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<IndexedDBAgent.DataEntry>, boolean)} callback
+     */
+    requestObjectStoreData: function(frameId, databaseName, objectStoreName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        this._requestData(frameId, databaseName, objectStoreName, "", idbKeyRange, skipCount, pageSize, callback);
+    },
+
+    /**
+     * @param {number} requestId
+     * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+     * @param {boolean} hasMore
+     */
+    _objectStoreDataLoaded: function(requestId, dataEntries, hasMore)
+    {
+        var request = this._requestDataCallbacks[requestId];
+        if (!request.callback)
+            return;
+
+        request.callback(dataEntries, hasMore);
+    },
+
+    /**
+     * @param {string} frameId
+     * @param {string} databaseName
+     * @param {string} objectStoreName
+     * @param {string} indexName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<IndexedDBAgent.DataEntry>, boolean)} callback
+     */
+    requestIndexData: function(frameId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        this._requestData(frameId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback);
+    },
+
+    /**
+     * @param {number} requestId
+     * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+     * @param {boolean} hasMore
+     */
+    _indexDataLoaded: function(requestId, dataEntries, hasMore)
+    {
+        var request = this._requestDataCallbacks[requestId];
+        if (!request.callback)
+            return;
+
+        request.callback(dataEntries, hasMore);
+    },
+
+    /**
      * @return {number}
      */
     _requestId: function()
@@ -415,11 +654,16 @@ WebInspector.IndexedDBRequestManager.prototype = {
             if (this._requestDatabaseNamesForFrameCallbacks[requestId].frameId === frameId)
                 delete this._requestDatabaseNamesForFrameCallbacks[requestId];
         }
-        
+
         for (var requestId in this._requestDatabaseCallbacks) {
             if (this._requestDatabaseCallbacks[requestId].frameId === frameId)
                 delete this._requestDatabaseCallbacks[requestId];
         }
+
+        for (var requestId in this._requestDataCallbacks) {
+            if (this._requestDataCallbacks[requestId].frameId === frameId)
+                delete this._requestDataCallbacks[requestId];
+        }
     },
 
     /**
@@ -431,19 +675,26 @@ WebInspector.IndexedDBRequestManager.prototype = {
             if (this._requestDatabaseCallbacks[requestId].frameId === frameId && this._requestDatabaseCallbacks[requestId].databaseName === databaseName)
                 delete this._requestDatabaseCallbacks[requestId];
         }
+
+        for (var requestId in this._requestDataCallbacks) {
+            if (this._requestDataCallbacks[requestId].frameId === frameId && this._requestDataCallbacks[requestId].databaseName === databaseName)
+                delete this._requestDataCallbacks[requestId];
+        }
     },
 
     _reset: function()
     {
         this._requestDatabaseNamesForFrameCallbacks = {};
         this._requestDatabaseCallbacks = {};
-
+        this._requestDataCallbacks = {};
     }
 }
 
 /**
  * @constructor
- */
+ * @param {string} frameId
+ * @param {function(IndexedDBAgent.SecurityOriginWithDatabaseNames)} callback
+*/
 WebInspector.IndexedDBRequestManager.DatabasesForFrameRequest = function(frameId, callback)
 {
     this.frameId = frameId;
@@ -452,6 +703,9 @@ WebInspector.IndexedDBRequestManager.DatabasesForFrameRequest = function(frameId
 
 /**
  * @constructor
+ * @param {string} frameId
+ * @param {string} databaseName
+ * @param {function(IndexedDBAgent.DatabaseWithObjectStores)} callback
  */
 WebInspector.IndexedDBRequestManager.DatabaseRequest = function(frameId, databaseName, callback)
 {
@@ -462,6 +716,23 @@ WebInspector.IndexedDBRequestManager.DatabaseRequest = function(frameId, databas
 
 /**
  * @constructor
+ * @param {string} frameId
+ * @param {string} databaseName
+ * @param {string} objectStoreName
+ * @param {string} indexName
+ * @param {function(Array.<IndexedDBAgent.DataEntry>, boolean)} callback
+ */
+WebInspector.IndexedDBRequestManager.DataRequest = function(frameId, databaseName, objectStoreName, indexName, callback)
+{
+    this.frameId = frameId;
+    this.databaseName = databaseName;
+    this.objectStoreName = objectStoreName;
+    this.indexName = indexName;
+    this.callback = callback;
+}
+
+/**
+ * @constructor
  * @implements {IndexedDBAgent.Dispatcher}
  * @param {WebInspector.IndexedDBRequestManager} indexedDBRequestManager
  */
@@ -487,5 +758,26 @@ WebInspector.IndexedDBDispatcher.prototype = {
     databaseLoaded: function(requestId, databaseWithObjectStores)
     {
         this._agentWrapper._databaseLoaded(requestId, databaseWithObjectStores);
+    },
+
+    /**
+     * @param {number} requestId
+     * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+     * @param {boolean} hasMore
+     */
+    objectStoreDataLoaded: function(requestId, dataEntries, hasMore)
+    {
+        this._agentWrapper._objectStoreDataLoaded(requestId, dataEntries, hasMore);
+    },
+
+    /**
+     * @param {number} requestId
+     * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+     * @param {boolean} hasMore
+     */
+    indexDataLoaded: function(requestId, dataEntries, hasMore)
+    {
+        this._agentWrapper._indexDataLoaded(requestId, dataEntries, hasMore);
     }
 }
+