summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-02 11:13:18 +0000
committerhans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-02 11:13:18 +0000
commit77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02 (patch)
treef5c8ce66b649844130f4683528ab8a0b61879307
parent5c6b568ce633302f64e45991648176b757b7134c (diff)
downloadchromium_src-77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02.zip
chromium_src-77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02.tar.gz
chromium_src-77931ff3077f06bbf4dd5c47ba59c7b4e1d28b02.tar.bz2
IndexedDB: Cursor pre-fetching.
Add support for cursor pre-fetching. BUG=88651 TEST=browser_tests Review URL: http://codereview.chromium.org/8662017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112675 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/test/data/indexeddb/common.js5
-rw-r--r--chrome/test/data/indexeddb/cursor_prefetch.html11
-rw-r--r--chrome/test/data/indexeddb/cursor_prefetch.js394
-rw-r--r--content/browser/in_process_webkit/indexed_db_browsertest.cc4
-rw-r--r--content/browser/in_process_webkit/indexed_db_callbacks.cc25
-rw-r--r--content/browser/in_process_webkit/indexed_db_callbacks.h4
-rw-r--r--content/browser/in_process_webkit/indexed_db_dispatcher_host.cc28
-rw-r--r--content/browser/in_process_webkit/indexed_db_dispatcher_host.h6
-rw-r--r--content/common/indexed_db_messages.h19
-rw-r--r--content/renderer/indexed_db_dispatcher.cc67
-rw-r--r--content/renderer/indexed_db_dispatcher.h18
-rw-r--r--content/renderer/renderer_webidbcursor_impl.cc100
-rw-r--r--content/renderer/renderer_webidbcursor_impl.h31
13 files changed, 710 insertions, 2 deletions
diff --git a/chrome/test/data/indexeddb/common.js b/chrome/test/data/indexeddb/common.js
index 5470fec..69665d0 100644
--- a/chrome/test/data/indexeddb/common.js
+++ b/chrome/test/data/indexeddb/common.js
@@ -4,7 +4,10 @@
function debug(message)
{
- document.getElementById('status').innerHTML += '<br/>' + message;
+ var span = document.createElement("span");
+ span.appendChild(document.createTextNode(message));
+ span.appendChild(document.createElement("br"));
+ document.getElementById('status').appendChild(span);
}
function done(message)
diff --git a/chrome/test/data/indexeddb/cursor_prefetch.html b/chrome/test/data/indexeddb/cursor_prefetch.html
new file mode 100644
index 0000000..d0728ee
--- /dev/null
+++ b/chrome/test/data/indexeddb/cursor_prefetch.html
@@ -0,0 +1,11 @@
+<html>
+ <head>
+ <title>IndexedDB cursor prefetch test</title>
+ <script type="text/javascript" src="common.js"></script>
+ <script type="text/javascript" src="cursor_prefetch.js"></script>
+ </head>
+ <body onLoad="test()">
+ <div id="status">Starting...</div>
+ </body>
+</html>
+
diff --git a/chrome/test/data/indexeddb/cursor_prefetch.js b/chrome/test/data/indexeddb/cursor_prefetch.js
new file mode 100644
index 0000000..927461f
--- /dev/null
+++ b/chrome/test/data/indexeddb/cursor_prefetch.js
@@ -0,0 +1,394 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// These constants should match the ones in renderer_webidbcursor_impl.h
+// to make sure the test hits the right code paths.
+var kPrefetchThreshold = 2;
+var kMinPrefetchAmount = 5;
+
+var kNumberOfItems = 200;
+
+function test() {
+ indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
+ IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction
+ IDBCursor = window.IDBCursor || window.webkitIDBCursor;
+ IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
+
+ request = indexedDB.open('cursor-prefetch');
+ request.onsuccess = openSuccess;
+ request.onerror = unexpectedErrorCallback;
+}
+
+function openSuccess() {
+ window.db = event.target.result;
+
+ request = db.setVersion('new version');
+ request.onsuccess = setVersionSuccess;
+ request.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess() {
+ debug("setVersionSuccess():");
+ window.trans = event.target.result;
+ shouldBeTrue("trans !== null");
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = fillObjectStore;
+
+ deleteAllObjectStores(db);
+ var store = db.createObjectStore('store');
+ store.createIndex('index', '');
+}
+
+function fillObjectStore() {
+ debug("fillObjectStore()");
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = firstTest;
+
+ var store = trans.objectStore('store');
+ debug("Storing " + kNumberOfItems + " object in the object store.");
+ for (var i = 0; i < kNumberOfItems; ++i) {
+ var req = store.put(i, i);
+ req.onerror = unexpectedErrorCallback;
+ }
+
+ // Let the transaction finish.
+}
+
+function firstTest() {
+ debug("firstTest()");
+
+ // Test iterating straight through the object store.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = secondTest;
+
+ var store = trans.objectStore('store');
+ var cursorReq = store.openCursor();
+ cursorReq.onerror = unexpectedErrorCallback;
+
+ count = 0;
+ cursorReq.onsuccess = function() {
+ cursor = event.target.result;
+ if (cursor === null) {
+ shouldBe("count", "kNumberOfItems");
+ return; // Let the transaction finish.
+ }
+
+ if (cursor.key !== count)
+ shouldBe("cursor.key", "count");
+ if (cursor.value !== count)
+ shouldBe("cursor.value", "count");
+
+ ++count;
+
+ cursor.continue();
+ }
+}
+
+function secondTest() {
+ debug("secondTest()");
+
+ // Test iterating through the object store, intermixed with
+ // continue calls to specific keys.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = thirdTest;
+
+ var store = trans.objectStore('store');
+ var cursorReq = store.openCursor();
+ cursorReq.onerror = unexpectedErrorCallback;
+
+ var jumpTable = [{from: 5, to: 17},
+ {from: 25, to: 30},
+ {from: 31, to: 35},
+ {from: 70, to: 80},
+ {from: 98, to: 99}];
+
+ count = 0;
+ expectedKey = 0;
+
+ cursorReq.onsuccess = function() {
+ cursor = event.target.result;
+ if (cursor === null) {
+ debug("Finished iterating after " + count + " steps.");
+ return; // Let the transaction finish.
+ }
+
+ if (cursor.key !== expectedKey)
+ shouldBe("cursor.key", "expectedKey");
+ if (cursor.value !== expectedKey)
+ shouldBe("cursor.value", "expectedKey");
+
+ ++count;
+
+ for (var i = 0; i < jumpTable.length; ++i) {
+ if (jumpTable[i].from === cursor.key) {
+ expectedKey = jumpTable[i].to;
+ debug("Jumping from "+ cursor.key + " to " + expectedKey);
+ cursor.continue(expectedKey);
+ return;
+ }
+ }
+
+ ++expectedKey;
+ cursor.continue();
+ }
+}
+
+function thirdTest() {
+ debug("thirdTest()");
+
+ // Test iterating straight through the object store in reverse.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = fourthTest;
+
+ var store = trans.objectStore('store');
+ var cursorReq = store.openCursor(
+ IDBKeyRange.upperBound(kNumberOfItems-1), IDBCursor.PREV);
+ cursorReq.onerror = unexpectedErrorCallback;
+
+ count = 0;
+ cursorReq.onsuccess = function() {
+ cursor = event.target.result;
+ if (cursor === null) {
+ shouldBe("count", "kNumberOfItems");
+ return; // Let the transaction finish.
+ }
+
+ expectedKey = kNumberOfItems - count - 1;
+
+ if (cursor.key !== expectedKey)
+ shouldBe("cursor.key", "expectedKey");
+ if (cursor.value !== expectedKey)
+ shouldBe("cursor.value", "expectedKey");
+
+ ++count;
+
+ cursor.continue();
+ }
+}
+
+function fourthTest() {
+ debug("fourthTest()");
+
+ // Test iterating, and then stopping before reaching the end.
+ // Make sure transaction terminates anyway.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = function() {
+ debug("fourthTest() transaction completed");
+ fifthTest();
+ }
+
+ var store = trans.objectStore('store');
+ var cursorReq = store.openCursor();
+ cursorReq.onerror = unexpectedErrorCallback;
+
+ count = 0;
+ cursorReq.onsuccess = function() {
+ cursor = event.target.result;
+
+ if (cursor.key !== count)
+ shouldBe("cursor.key", "count");
+ if (cursor.value !== count)
+ shouldBe("cursor.value", "count");
+
+ ++count;
+
+ if (count === 25) {
+ // Schedule some other request.
+ var otherReq = store.get(42);
+ otherReq.onerror = unexpectedErrorCallback;
+ otherReq.onsuccess = function() {
+ if (count === 25) {
+ debug("Other request fired before continue, as expected.");
+ } else {
+ debug("Other request fired out-of-order!");
+ fail();
+ }
+ }
+
+ cursor.continue();
+ return;
+ }
+
+ if (count === 30) {
+ // Do a continue first, then another request.
+ cursor.continue();
+
+ var otherReq = store.get(42);
+ otherReq.onerror = unexpectedErrorCallback;
+ otherReq.onsuccess = function() {
+ if (count === 31) {
+ debug("Other request fired right after continue as expected.");
+ } else {
+ debug("Other request didn't fire right after continue as expected.");
+ fail();
+ }
+ }
+
+ return;
+ }
+
+ if (count === 75) {
+ return; // Sudden stop.
+ }
+
+ cursor.continue();
+ }
+}
+
+function fifthTest() {
+ debug("fifthTest()");
+
+ // Test iterating over the pre-fetch threshold, but make sure the
+ // cursor is positioned so that it is actually at the last element
+ // in the range when pre-fetch fires, and make sure a null cursor
+ // is the result as expected.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = sixthTest;
+
+ var store = trans.objectStore('store');
+
+ var startKey = kNumberOfItems - 1 - kPrefetchThreshold;
+ var cursorReq = store.openCursor(IDBKeyRange.lowerBound(startKey));
+ cursorReq.onerror = unexpectedErrorCallback;
+
+ count = 0;
+ cursorReq.onsuccess = function() {
+ cursor = event.target.result;
+
+ if (cursor === null) {
+ debug("cursor is null");
+ shouldBe("count", "kPrefetchThreshold + 1");
+ return;
+ }
+
+ debug("count: " + count);
+ ++count;
+ cursor.continue();
+ }
+}
+
+function sixthTest() {
+ debug("sixthTest()");
+
+ // Test stepping two cursors simultaneously. First cursor1 steps
+ // for a while, then cursor2, then back to cursor1, etc.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = seventhTest;
+ var store = trans.objectStore('store');
+
+ cursor1 = null;
+ cursor2 = null;
+
+ count1 = 0;
+ count2 = 0;
+
+ var cursor1func = function() {
+ var cursor = event.target.result;
+ if (cursor === null) {
+ shouldBe("count1", "kNumberOfItems");
+ cursor2.continue();
+ return;
+ }
+
+ if (cursor1 === null) {
+ cursor1 = cursor;
+ }
+
+ if (cursor1.key !== count1)
+ shouldBe("cursor1.key", "count1");
+ if (cursor1.value !== count1)
+ shouldBe("cursor1.value", "count1");
+
+ ++count1;
+
+ if (count1 % 20 === 0) {
+ if (cursor2 !== null) {
+ cursor2.continue();
+ } else {
+ var req = store.openCursor();
+ req.onerror = unexpectedErrorCallback;
+ req.onsuccess = cursor2func;
+ }
+ } else {
+ cursor1.continue();
+ }
+ }
+
+ var cursor2func = function() {
+ var cursor = event.target.result;
+ if (cursor === null) {
+ shouldBe("count2", "kNumberOfItems");
+ return;
+ }
+
+ if (cursor2 === null) {
+ cursor2 = cursor;
+ }
+
+ if (cursor2.key !== count2)
+ shouldBe("cursor2.key", "count2");
+ if (cursor2.value !== count2)
+ shouldBe("cursor2.value", "count2");
+
+ ++count2;
+
+ if (count2 % 20 === 0) {
+ cursor1.continue();
+ } else {
+ cursor2.continue();
+ }
+ }
+
+ var req = store.openCursor();
+ req.onerror = unexpectedErrorCallback;
+ req.onsuccess = cursor1func;
+}
+
+function seventhTest() {
+ debug("seventhTest()");
+
+ // Test iterating straight through an index.
+
+ var trans = db.transaction(['store'], IDBTransaction.READ_WRITE);
+ trans.onabort = unexpectedAbortCallback;
+ trans.oncomplete = done;
+
+ var store = trans.objectStore('store');
+ var index = store.index('index');
+
+ var cursorReq = index.openCursor();
+ cursorReq.onerror = unexpectedErrorCallback;
+ count = 0;
+
+ cursorReq.onsuccess = function() {
+ cursor = event.target.result;
+ if (cursor === null) {
+ shouldBe("count", "kNumberOfItems");
+ return;
+ }
+
+ if (cursor.key !== count)
+ shouldBe("cursor.key", "count");
+ if (cursor.primaryKey !== count)
+ shouldBe("cursor.primaryKey", "count");
+ if (cursor.value !== count)
+ shouldBe("cursor.value", "count");
+
+ ++count;
+ cursor.continue();
+ }
+}
diff --git a/content/browser/in_process_webkit/indexed_db_browsertest.cc b/content/browser/in_process_webkit/indexed_db_browsertest.cc
index c849a18..759efda 100644
--- a/content/browser/in_process_webkit/indexed_db_browsertest.cc
+++ b/content/browser/in_process_webkit/indexed_db_browsertest.cc
@@ -69,6 +69,10 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) {
true /* incognito */);
}
+IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) {
+ SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("cursor_prefetch.html"))));
+}
+
// Flaky: http://crbug.com/70773
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DISABLED_IndexTest) {
SimpleTest(testUrl(FilePath(FILE_PATH_LITERAL("index_test.html"))));
diff --git a/content/browser/in_process_webkit/indexed_db_callbacks.cc b/content/browser/in_process_webkit/indexed_db_callbacks.cc
index 70e79ef..969a430 100644
--- a/content/browser/in_process_webkit/indexed_db_callbacks.cc
+++ b/content/browser/in_process_webkit/indexed_db_callbacks.cc
@@ -59,6 +59,31 @@ void IndexedDBCallbacks<WebKit::WebIDBCursor>::onSuccessWithContinuation() {
content::SerializedScriptValue(idb_cursor->value())));
}
+void IndexedDBCallbacks<WebKit::WebIDBCursor>::onSuccessWithPrefetch(
+ const WebKit::WebVector<WebKit::WebIDBKey>& keys,
+ const WebKit::WebVector<WebKit::WebIDBKey>& primaryKeys,
+ const WebKit::WebVector<WebKit::WebSerializedScriptValue>& values) {
+ DCHECK(cursor_id_ != -1);
+
+ std::vector<IndexedDBKey> msgKeys;
+ std::vector<IndexedDBKey> msgPrimaryKeys;
+ std::vector<content::SerializedScriptValue> msgValues;
+
+ for (size_t i = 0; i < keys.size(); ++i) {
+ msgKeys.push_back(IndexedDBKey(keys[i]));
+ msgPrimaryKeys.push_back(IndexedDBKey(primaryKeys[i]));
+ msgValues.push_back(content::SerializedScriptValue(values[i]));
+ }
+
+ dispatcher_host()->Send(
+ new IndexedDBMsg_CallbacksSuccessCursorPrefetch(
+ response_id(),
+ cursor_id_,
+ msgKeys,
+ msgPrimaryKeys,
+ msgValues));
+}
+
void IndexedDBCallbacks<WebKit::WebIDBKey>::onSuccess(
const WebKit::WebIDBKey& value) {
dispatcher_host()->Send(
diff --git a/content/browser/in_process_webkit/indexed_db_callbacks.h b/content/browser/in_process_webkit/indexed_db_callbacks.h
index 085bfa4..de9edaf 100644
--- a/content/browser/in_process_webkit/indexed_db_callbacks.h
+++ b/content/browser/in_process_webkit/indexed_db_callbacks.h
@@ -93,6 +93,10 @@ class IndexedDBCallbacks<WebKit::WebIDBCursor>
virtual void onSuccess(WebKit::WebIDBCursor* idb_object);
virtual void onSuccess(const WebKit::WebSerializedScriptValue& value);
virtual void onSuccessWithContinuation();
+ virtual void onSuccessWithPrefetch(
+ const WebKit::WebVector<WebKit::WebIDBKey>& keys,
+ const WebKit::WebVector<WebKit::WebIDBKey>& primaryKeys,
+ const WebKit::WebVector<WebKit::WebSerializedScriptValue>& values);
private:
// The id of the cursor this callback concerns, or -1 if the cursor
diff --git a/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc b/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc
index e4abb51..f521667 100644
--- a/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc
+++ b/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc
@@ -886,6 +886,8 @@ bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDirection, OnDirection)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorUpdate, OnUpdate)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDelete, OnDelete)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -967,6 +969,32 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
cursor_id), *ec);
}
+void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
+ int32 cursor_id,
+ int32 response_id,
+ int n,
+ WebKit::WebExceptionCode* ec) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
+ WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_, cursor_id);
+ if (!idb_cursor)
+ return;
+
+ *ec = 0;
+ idb_cursor->prefetchContinue(
+ n, new IndexedDBCallbacks<WebIDBCursor>(parent_, response_id,
+ cursor_id), *ec);
+}
+
+void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
+ int32 cursor_id, int used_prefetches, int unused_prefetches) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
+ WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_, cursor_id);
+ if (!idb_cursor)
+ return;
+
+ idb_cursor->prefetchReset(used_prefetches, unused_prefetches);
+}
+
void IndexedDBDispatcherHost::CursorDispatcherHost::OnDelete(
int32 cursor_id,
int32 response_id,
diff --git a/content/browser/in_process_webkit/indexed_db_dispatcher_host.h b/content/browser/in_process_webkit/indexed_db_dispatcher_host.h
index 4ee03b9..88bff96 100644
--- a/content/browser/in_process_webkit/indexed_db_dispatcher_host.h
+++ b/content/browser/in_process_webkit/indexed_db_dispatcher_host.h
@@ -238,6 +238,12 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
int32 response_id,
const IndexedDBKey& key,
WebKit::WebExceptionCode* ec);
+ void OnPrefetch(int32 idb_cursor_id,
+ int32 response_id,
+ int n,
+ WebKit::WebExceptionCode* ec);
+ void OnPrefetchReset(int32 idb_cursor_id, int used_prefetches,
+ int unused_prefetches);
void OnDelete(int32 idb_object_store_id,
int32 response_id,
WebKit::WebExceptionCode* ec);
diff --git a/content/common/indexed_db_messages.h b/content/common/indexed_db_messages.h
index 118106e..31a0974 100644
--- a/content/common/indexed_db_messages.h
+++ b/content/common/indexed_db_messages.h
@@ -147,6 +147,12 @@ IPC_MESSAGE_CONTROL5(IndexedDBMsg_CallbacksSuccessCursorContinue,
IndexedDBKey /* key */,
IndexedDBKey /* primary key */,
content::SerializedScriptValue /* script_value */)
+IPC_MESSAGE_CONTROL5(IndexedDBMsg_CallbacksSuccessCursorPrefetch,
+ int32 /* response_id */,
+ int32 /* cursor_id */,
+ std::vector<IndexedDBKey> /* keys */,
+ std::vector<IndexedDBKey> /* primary keys */,
+ std::vector<content::SerializedScriptValue> /* values */)
IPC_MESSAGE_CONTROL2(IndexedDBMsg_CallbacksSuccessIDBDatabase,
int32 /* response_id */,
int32 /* idb_database_id */)
@@ -200,6 +206,19 @@ IPC_SYNC_MESSAGE_CONTROL3_1(IndexedDBHostMsg_CursorContinue,
IndexedDBKey, /* key */
WebKit::WebExceptionCode /* ec */)
+// WebIDBCursor::prefetchContinue() message.
+IPC_SYNC_MESSAGE_CONTROL3_1(IndexedDBHostMsg_CursorPrefetch,
+ int32, /* idb_cursor_id */
+ int32, /* response_id */
+ int32, /* n */
+ WebKit::WebExceptionCode /* ec */)
+
+// WebIDBCursor::prefetchReset() message.
+IPC_SYNC_MESSAGE_CONTROL3_0(IndexedDBHostMsg_CursorPrefetchReset,
+ int32, /* idb_cursor_id */
+ int32, /* used_prefetches */
+ int32 /* used_prefetches */)
+
// WebIDBCursor::remove() message.
IPC_SYNC_MESSAGE_CONTROL2_1(IndexedDBHostMsg_CursorDelete,
int32, /* idb_cursor_id */
diff --git a/content/renderer/indexed_db_dispatcher.cc b/content/renderer/indexed_db_dispatcher.cc
index 8b3ddda..2308e8b 100644
--- a/content/renderer/indexed_db_dispatcher.cc
+++ b/content/renderer/indexed_db_dispatcher.cc
@@ -44,6 +44,8 @@ bool IndexedDBDispatcher::OnMessageReceived(const IPC::Message& msg) {
OnSuccessOpenCursor)
IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorContinue,
OnSuccessCursorContinue)
+ IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorPrefetch,
+ OnSuccessCursorPrefetch)
IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIDBDatabase,
OnSuccessIDBDatabase)
IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIndexedDBKey,
@@ -74,6 +76,7 @@ void IndexedDBDispatcher::RequestIDBCursorUpdate(
WebIDBCallbacks* callbacks_ptr,
int32 idb_cursor_id,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -88,6 +91,9 @@ void IndexedDBDispatcher::RequestIDBCursorContinue(
WebIDBCallbacks* callbacks_ptr,
int32 idb_cursor_id,
WebExceptionCode* ec) {
+ // Reset all cursor prefetch caches except for this cursor.
+ ResetCursorPrefetchCaches(idb_cursor_id);
+
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -97,10 +103,32 @@ void IndexedDBDispatcher::RequestIDBCursorContinue(
pending_callbacks_.Remove(response_id);
}
+void IndexedDBDispatcher::RequestIDBCursorPrefetch(
+ int n,
+ WebIDBCallbacks* callbacks_ptr,
+ int32 idb_cursor_id,
+ WebExceptionCode* ec) {
+ scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
+
+ int32 response_id = pending_callbacks_.Add(callbacks.release());
+ RenderThreadImpl::current()->Send(
+ new IndexedDBHostMsg_CursorPrefetch(idb_cursor_id, response_id, n, ec));
+ if (*ec)
+ pending_callbacks_.Remove(response_id);
+}
+
+void IndexedDBDispatcher::RequestIDBCursorPrefetchReset(
+ int used_prefetches, int unused_prefetches, int32 idb_cursor_id) {
+ RenderThreadImpl::current()->Send(
+ new IndexedDBHostMsg_CursorPrefetchReset(idb_cursor_id, used_prefetches,
+ unused_prefetches));
+}
+
void IndexedDBDispatcher::RequestIDBCursorDelete(
WebIDBCallbacks* callbacks_ptr,
int32 idb_cursor_id,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -114,6 +142,7 @@ void IndexedDBDispatcher::RequestIDBFactoryOpen(
WebIDBCallbacks* callbacks_ptr,
const string16& origin,
WebFrame* web_frame) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
if (!web_frame)
@@ -133,6 +162,7 @@ void IndexedDBDispatcher::RequestIDBFactoryGetDatabaseNames(
WebIDBCallbacks* callbacks_ptr,
const string16& origin,
WebFrame* web_frame) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
if (!web_frame)
@@ -152,6 +182,7 @@ void IndexedDBDispatcher::RequestIDBFactoryDeleteDatabase(
WebIDBCallbacks* callbacks_ptr,
const string16& origin,
WebFrame* web_frame) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
if (!web_frame)
@@ -168,6 +199,7 @@ void IndexedDBDispatcher::RequestIDBFactoryDeleteDatabase(
}
void IndexedDBDispatcher::RequestIDBDatabaseClose(int32 idb_database_id) {
+ ResetCursorPrefetchCaches();
Send(new IndexedDBHostMsg_DatabaseClose(idb_database_id));
pending_database_callbacks_.Remove(idb_database_id);
}
@@ -175,6 +207,7 @@ void IndexedDBDispatcher::RequestIDBDatabaseClose(int32 idb_database_id) {
void IndexedDBDispatcher::RequestIDBDatabaseOpen(
WebIDBDatabaseCallbacks* callbacks_ptr,
int32 idb_database_id) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBDatabaseCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_database_callbacks_.Add(callbacks.release());
@@ -186,6 +219,7 @@ void IndexedDBDispatcher::RequestIDBDatabaseSetVersion(
WebIDBCallbacks* callbacks_ptr,
int32 idb_database_id,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -202,6 +236,7 @@ void IndexedDBDispatcher::RequestIDBIndexOpenObjectCursor(
int32 idb_index_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
IndexedDBHostMsg_IndexOpenCursor_Params params;
params.response_id = pending_callbacks_.Add(callbacks.release());
@@ -224,6 +259,7 @@ void IndexedDBDispatcher::RequestIDBIndexOpenKeyCursor(
int32 idb_index_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
IndexedDBHostMsg_IndexOpenCursor_Params params;
params.response_id = pending_callbacks_.Add(callbacks.release());
@@ -247,6 +283,7 @@ void IndexedDBDispatcher::RequestIDBIndexGetObject(
int32 idb_index_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
Send(new IndexedDBHostMsg_IndexGetObject(idb_index_id, response_id, key,
@@ -261,6 +298,7 @@ void IndexedDBDispatcher::RequestIDBIndexGetKey(
int32 idb_index_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
Send(new IndexedDBHostMsg_IndexGetKey(
@@ -276,6 +314,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreGet(
int32 idb_object_store_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -294,6 +333,7 @@ void IndexedDBDispatcher::RequestIDBObjectStorePut(
int32 idb_object_store_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
IndexedDBHostMsg_ObjectStorePut_Params params;
params.idb_object_store_id = idb_object_store_id;
@@ -313,6 +353,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreDelete(
int32 idb_object_store_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -327,6 +368,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreClear(
int32 idb_object_store_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
int32 response_id = pending_callbacks_.Add(callbacks.release());
@@ -343,6 +385,7 @@ void IndexedDBDispatcher::RequestIDBObjectStoreOpenCursor(
int32 idb_object_store_id,
const WebIDBTransaction& transaction,
WebExceptionCode* ec) {
+ ResetCursorPrefetchCaches();
scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
IndexedDBHostMsg_ObjectStoreOpenCursor_Params params;
params.response_id = pending_callbacks_.Add(callbacks.release());
@@ -458,6 +501,21 @@ void IndexedDBDispatcher::OnSuccessCursorContinue(
pending_callbacks_.Remove(response_id);
}
+void IndexedDBDispatcher::OnSuccessCursorPrefetch(
+ int32 response_id,
+ int32 cursor_id,
+ const std::vector<IndexedDBKey>& keys,
+ const std::vector<IndexedDBKey>& primary_keys,
+ const std::vector<content::SerializedScriptValue>& values) {
+ RendererWebIDBCursorImpl* cursor = cursors_[cursor_id];
+ DCHECK(cursor);
+ cursor->SetPrefetchData(keys, primary_keys, values);
+
+ WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(response_id);
+ cursor->CachedContinue(callbacks);
+ pending_callbacks_.Remove(response_id);
+}
+
void IndexedDBDispatcher::OnBlocked(int32 response_id) {
WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(response_id);
callbacks->onBlocked();
@@ -500,3 +558,12 @@ void IndexedDBDispatcher::OnVersionChange(int32 database_id,
return;
callbacks->onVersionChange(newVersion);
}
+
+void IndexedDBDispatcher::ResetCursorPrefetchCaches(int32 exception_cursor_id) {
+ typedef std::map<int32, RendererWebIDBCursorImpl*>::iterator Iterator;
+ for (Iterator i = cursors_.begin(); i != cursors_.end(); ++i) {
+ if (i->first == exception_cursor_id)
+ continue;
+ i->second->ResetPrefetchCache();
+ }
+}
diff --git a/content/renderer/indexed_db_dispatcher.h b/content/renderer/indexed_db_dispatcher.h
index d2b3f1a..6f7deef 100644
--- a/content/renderer/indexed_db_dispatcher.h
+++ b/content/renderer/indexed_db_dispatcher.h
@@ -70,6 +70,15 @@ class IndexedDBDispatcher : public IPC::Channel::Listener {
int32 idb_cursor_id,
WebKit::WebExceptionCode* ec);
+ void RequestIDBCursorPrefetch(
+ int n,
+ WebKit::WebIDBCallbacks* callbacks_ptr,
+ int32 idb_cursor_id,
+ WebKit::WebExceptionCode* ec);
+
+ void RequestIDBCursorPrefetchReset(int used_prefetches, int unused_prefetches,
+ int32 idb_cursor_id);
+
void RequestIDBCursorDelete(
WebKit::WebIDBCallbacks* callbacks_ptr,
int32 idb_cursor_id,
@@ -174,6 +183,12 @@ class IndexedDBDispatcher : public IPC::Channel::Listener {
const IndexedDBKey& key,
const IndexedDBKey& primary_key,
const content::SerializedScriptValue& value);
+ void OnSuccessCursorPrefetch(
+ int32 response_id,
+ int32 cursor_id,
+ const std::vector<IndexedDBKey>& keys,
+ const std::vector<IndexedDBKey>& primary_keys,
+ const std::vector<content::SerializedScriptValue>& values);
void OnSuccessStringList(int32 response_id,
const std::vector<string16>& value);
void OnSuccessSerializedScriptValue(
@@ -185,6 +200,9 @@ class IndexedDBDispatcher : public IPC::Channel::Listener {
void OnComplete(int32 transaction_id);
void OnVersionChange(int32 database_id, const string16& newVersion);
+ // Reset cursor prefetch caches for all cursors except exception_cursor_id.
+ void ResetCursorPrefetchCaches(int32 exception_cursor_id = -1);
+
// Careful! WebIDBCallbacks wraps non-threadsafe data types. It must be
// destroyed and used on the same thread it was created on.
IDMap<WebKit::WebIDBCallbacks, IDMapOwnPointer> pending_callbacks_;
diff --git a/content/renderer/renderer_webidbcursor_impl.cc b/content/renderer/renderer_webidbcursor_impl.cc
index 5ba7820..58113ad 100644
--- a/content/renderer/renderer_webidbcursor_impl.cc
+++ b/content/renderer/renderer_webidbcursor_impl.cc
@@ -14,7 +14,11 @@ using WebKit::WebIDBKey;
using WebKit::WebSerializedScriptValue;
RendererWebIDBCursorImpl::RendererWebIDBCursorImpl(int32 idb_cursor_id)
- : idb_cursor_id_(idb_cursor_id) {
+ : idb_cursor_id_(idb_cursor_id),
+ continue_count_(0),
+ used_prefetches_(0),
+ pending_onsuccess_callbacks_(0),
+ prefetch_amount_(kMinPrefetchAmount) {
}
RendererWebIDBCursorImpl::~RendererWebIDBCursorImpl() {
@@ -62,6 +66,34 @@ void RendererWebIDBCursorImpl::continueFunction(const WebIDBKey& key,
WebExceptionCode& ec) {
IndexedDBDispatcher* dispatcher =
RenderThreadImpl::current()->indexed_db_dispatcher();
+
+ if (key.type() == WebIDBKey::InvalidType) {
+ // No key, so this would qualify for a prefetch.
+ ++continue_count_;
+
+ if (!prefetch_keys_.empty()) {
+ // We have a prefetch cache, so serve the result from that.
+ CachedContinue(callbacks);
+ return;
+ }
+
+ if (continue_count_ > kPrefetchContinueThreshold) {
+ // Request pre-fetch.
+ dispatcher->RequestIDBCursorPrefetch(prefetch_amount_, callbacks,
+ idb_cursor_id_, &ec);
+
+ // Increase prefetch_amount_ exponentially.
+ prefetch_amount_ *= 2;
+ if (prefetch_amount_ > kMaxPrefetchAmount)
+ prefetch_amount_ = kMaxPrefetchAmount;
+
+ return;
+ }
+ } else {
+ // Key argument supplied. We couldn't prefetch this.
+ ResetPrefetchCache();
+ }
+
dispatcher->RequestIDBCursorContinue(IndexedDBKey(key), callbacks,
idb_cursor_id_, &ec);
}
@@ -73,6 +105,20 @@ void RendererWebIDBCursorImpl::deleteFunction(WebIDBCallbacks* callbacks,
dispatcher->RequestIDBCursorDelete(callbacks, idb_cursor_id_, &ec);
}
+void RendererWebIDBCursorImpl::postSuccessHandlerCallback()
+{
+ pending_onsuccess_callbacks_--;
+
+ // If the onsuccess callback called continue() on the cursor again,
+ // and that continue was served by the prefetch cache, then
+ // pending_onsuccess_callbacks_ would be incremented.
+ // If not, it means the callback did something else, or nothing at all,
+ // in which case we need to reset the cache.
+
+ if (pending_onsuccess_callbacks_ == 0)
+ ResetPrefetchCache();
+}
+
void RendererWebIDBCursorImpl::SetKeyAndValue(
const IndexedDBKey& key,
const IndexedDBKey& primary_key,
@@ -81,3 +127,55 @@ void RendererWebIDBCursorImpl::SetKeyAndValue(
primary_key_ = primary_key;
value_ = value;
}
+
+void RendererWebIDBCursorImpl::SetPrefetchData(
+ const std::vector<IndexedDBKey>& keys,
+ const std::vector<IndexedDBKey>& primary_keys,
+ const std::vector<content::SerializedScriptValue>& values) {
+ prefetch_keys_.assign(keys.begin(), keys.end());
+ prefetch_primary_keys_.assign(primary_keys.begin(), primary_keys.end());
+ prefetch_values_.assign(values.begin(), values.end());
+
+ used_prefetches_ = 0;
+ pending_onsuccess_callbacks_ = 0;
+}
+
+void RendererWebIDBCursorImpl::CachedContinue(
+ WebKit::WebIDBCallbacks* callbacks) {
+ DCHECK(prefetch_keys_.size() > 0);
+ DCHECK(prefetch_primary_keys_.size() == prefetch_keys_.size());
+ DCHECK(prefetch_values_.size() == prefetch_keys_.size());
+
+ key_ = prefetch_keys_.front();
+ primary_key_ = prefetch_primary_keys_.front();
+ value_ = prefetch_values_.front();
+
+ prefetch_keys_.pop_front();
+ prefetch_primary_keys_.pop_front();
+ prefetch_values_.pop_front();
+ used_prefetches_++;
+
+ pending_onsuccess_callbacks_++;
+ callbacks->onSuccessWithContinuation();
+}
+
+void RendererWebIDBCursorImpl::ResetPrefetchCache() {
+ continue_count_ = 0;
+ prefetch_amount_ = kMinPrefetchAmount;
+
+ if (!prefetch_keys_.size()) {
+ // No prefetch cache, so no need to reset the cursor in the back-end.
+ return;
+ }
+
+ IndexedDBDispatcher* dispatcher =
+ RenderThreadImpl::current()->indexed_db_dispatcher();
+ dispatcher->RequestIDBCursorPrefetchReset(used_prefetches_,
+ prefetch_keys_.size(),
+ idb_cursor_id_);
+ prefetch_keys_.clear();
+ prefetch_primary_keys_.clear();
+ prefetch_values_.clear();
+
+ pending_onsuccess_callbacks_ = 0;
+}
diff --git a/content/renderer/renderer_webidbcursor_impl.h b/content/renderer/renderer_webidbcursor_impl.h
index fbca6b2..b033990 100644
--- a/content/renderer/renderer_webidbcursor_impl.h
+++ b/content/renderer/renderer_webidbcursor_impl.h
@@ -5,6 +5,8 @@
#ifndef CONTENT_RENDERER_RENDERER_WEBIDBCURSOR_IMPL_H_
#define CONTENT_RENDERER_RENDERER_WEBIDBCURSOR_IMPL_H_
+#include <deque>
+
#include "base/basictypes.h"
#include "content/common/indexed_db_key.h"
#include "content/public/common/serialized_script_value.h"
@@ -30,15 +32,44 @@ class RendererWebIDBCursorImpl : public WebKit::WebIDBCursor {
WebKit::WebExceptionCode& ec);
virtual void deleteFunction(WebKit::WebIDBCallbacks* callback,
WebKit::WebExceptionCode& ec);
+ virtual void postSuccessHandlerCallback();
void SetKeyAndValue(const IndexedDBKey& key, const IndexedDBKey& primary_key,
const content::SerializedScriptValue& value);
+ void SetPrefetchData(
+ const std::vector<IndexedDBKey>& keys,
+ const std::vector<IndexedDBKey>& primary_keys,
+ const std::vector<content::SerializedScriptValue>& values);
+
+ void CachedContinue(WebKit::WebIDBCallbacks* callbacks);
+ void ResetPrefetchCache();
private:
int32 idb_cursor_id_;
IndexedDBKey key_;
IndexedDBKey primary_key_;
content::SerializedScriptValue value_;
+
+ // Prefetch cache.
+ std::deque<IndexedDBKey> prefetch_keys_;
+ std::deque<IndexedDBKey> prefetch_primary_keys_;
+ std::deque<content::SerializedScriptValue> prefetch_values_;
+
+ // Number of continue calls that would qualify for a pre-fetch.
+ int continue_count_;
+
+ // Number of items used from the last prefetch.
+ int used_prefetches_;
+
+ // Number of onsuccess handlers we are waiting for.
+ int pending_onsuccess_callbacks_;
+
+ // Number of items to request in next prefetch.
+ int prefetch_amount_;
+
+ enum { kPrefetchContinueThreshold = 2 };
+ enum { kMinPrefetchAmount = 5 };
+ enum { kMaxPrefetchAmount = 100 };
};
#endif // CONTENT_RENDERER_RENDERER_WEBIDBCURSOR_IMPL_H_