<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[163662] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/163662">163662</a></dd>
<dt>Author</dt> <dd>beidson@apple.com</dd>
<dt>Date</dt> <dd>2014-02-07 16:30:50 -0800 (Fri, 07 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>IDB: Some Mozilla cursor mutation tests fail
&lt;rdar://problem/16011680&gt; and https://bugs.webkit.org/show_bug.cgi?id=128374

Reviewed by Sam Weinig.

Source/WebCore:

Tested by:
storage/indexeddb/mozilla/cursor-mutation-objectstore-only.html
storage/indexeddb/mozilla/cursor-mutation.html

The previous comment about LevelDB was wrong.
Apparently calling onSuccess() with a null SharedBuffer means the cursor reached the end.
Update to distinguish between an error and reaching the end:
* Modules/indexeddb/IDBCursorBackendOperations.cpp:
(WebCore::CursorAdvanceOperation::perform):
(WebCore::CursorIterationOperation::perform):

Add a new IDBKey type - Maximum - To be used in comparisons between keys.
This will allow the DatabaseProcess to operate on the range of all keys:
* Modules/indexeddb/IDBKey.cpp:
(WebCore::IDBKey::compare):
* Modules/indexeddb/IDBKey.h:

* Modules/indexeddb/IDBKeyData.cpp:
(WebCore::IDBKeyData::IDBKeyData):
(WebCore::IDBKeyData::maybeCreateIDBKey):
(WebCore::IDBKeyData::isolatedCopy):
(WebCore::IDBKeyData::encode):
(WebCore::IDBKeyData::decode):
(WebCore::IDBKeyData::compare):
(WebCore::IDBKeyData::loggingString):
* Modules/indexeddb/IDBKeyData.h:
(WebCore::IDBKeyData::minimum):
(WebCore::IDBKeyData::maximum):

* bindings/js/IDBBindingUtilities.cpp:
(WebCore::idbKeyToJSValue):

Remove an unused callback:
* Modules/indexeddb/IDBCallbacks.h:
* Modules/indexeddb/IDBRequest.h:

Source/WebKit2:

After a SQLite statement is prepared, stepping it will not pick up any subsequent changes
to the object store.

By keeping track of the current record we’re looking at in the object store and being told
that the object store changed its contents, we can reset the statement to pick up where it
left off but with the new object store contents.

* DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp:
(WebKit::UniqueIDBDatabase::putRecordInBackingStore): Tell the backing store to notify
  its cursors that the object store changed its records.
(WebKit::UniqueIDBDatabase::deleteRangeInBackingStore): Ditto.

* DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h:
* DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp:
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::notifyCursorsOfChanges): Tell the transaction
  to notify its cursors that the object store changed its records.
* DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h:

* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp:
(WebKit::SQLiteIDBTransaction::notifyCursorsOfChanges): Tell the relevant cursors that their
  object store changed its records.
* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h:

* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp:
(WebKit::SQLiteIDBCursor::SQLiteIDBCursor):
(WebKit::getIndexStatement): All statements now have a lower and upper range. By default we bind
  IDBKey::Minimum to the lower range and IDBKey::Maximum to the upper range.
(WebKit::getObjectStoreStatement): Ditto.
(WebKit::SQLiteIDBCursor::establishStatement):
(WebKit::SQLiteIDBCursor::createSQLiteStatement):
(WebKit::SQLiteIDBCursor::objectStoreRecordsChanged): Set the flag indicating the statement
  needs to be reset and rebound.
(WebKit::SQLiteIDBCursor::resetAndRebindStatement):
(WebKit::SQLiteIDBCursor::bindArguments): Factored out to be shared between statement prepare()
  and statement reset().
(WebKit::SQLiteIDBCursor::advance):
(WebKit::SQLiteIDBCursor::advanceOnce): If the statement needs to be reset, do so before advancing it.
(WebKit::SQLiteIDBCursor::internalAdvanceOnce): In a few cases, tell advanceOnce that it needs to
  try again because it is on a missing record or is repeating a record.
* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h:
(WebKit::SQLiteIDBCursor::objectStoreID):

Make sure Maximum and Minimum keys are never sent across IPC, as that doesn’t make sense:
* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder&lt;IDBKeyData&gt;::encode):
(IPC::ArgumentCoder&lt;IDBKeyData&gt;::decode):

LayoutTests:

* platform/mac-wk2/TestExpectations: Add two tests that now pass.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformmacwk2TestExpectations">trunk/LayoutTests/platform/mac-wk2/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBCallbacksh">trunk/Source/WebCore/Modules/indexeddb/IDBCallbacks.h</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBCursorBackendOperationscpp">trunk/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBKeycpp">trunk/Source/WebCore/Modules/indexeddb/IDBKey.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBKeyh">trunk/Source/WebCore/Modules/indexeddb/IDBKey.h</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBKeyDatacpp">trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBKeyDatah">trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbIDBRequesth">trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h</a></li>
<li><a href="#trunkSourceWebCorebindingsjsIDBBindingUtilitiescpp">trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBUniqueIDBDatabasecpp">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBUniqueIDBDatabaseBackingStoreh">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBCursorcpp">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBCursorh">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBTransactioncpp">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBTransactionh">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBsqliteUniqueIDBDatabaseBackingStoreSQLitecpp">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp</a></li>
<li><a href="#trunkSourceWebKit2DatabaseProcessIndexedDBsqliteUniqueIDBDatabaseBackingStoreSQLiteh">trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h</a></li>
<li><a href="#trunkSourceWebKit2SharedWebCoreArgumentCoderscpp">trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/LayoutTests/ChangeLog        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -1,3 +1,12 @@
</span><ins>+2014-02-07  Brady Eidson  &lt;beidson@apple.com&gt;
+
+        IDB: Some Mozilla cursor mutation tests fail
+        &lt;rdar://problem/16011680&gt; and https://bugs.webkit.org/show_bug.cgi?id=128374
+
+        Reviewed by Sam Weinig.
+
+        * platform/mac-wk2/TestExpectations: Add two tests that now pass.
+
</ins><span class="cx"> 2014-02-07  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Revert r154384 and r154674 as they broke selection rect computations for text with ligatures.
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacwk2TestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac-wk2/TestExpectations (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac-wk2/TestExpectations        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/LayoutTests/platform/mac-wk2/TestExpectations        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -475,6 +475,8 @@
</span><span class="cx"> storage/indexeddb/mozilla/autoincrement-indexes.html [ Pass ]
</span><span class="cx"> storage/indexeddb/mozilla/clear.html [ Pass ]
</span><span class="cx"> storage/indexeddb/mozilla/create-index-with-integer-keys.html [ Pass ]
</span><ins>+storage/indexeddb/mozilla/cursor-mutation-objectstore-only.html [ Pass ]
+storage/indexeddb/mozilla/cursor-mutation.html [ Pass ]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> ### END OF (5) Features that are not supported in WebKit1, so skipped in mac/TestExpectations then re-enabled here
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/ChangeLog        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -1,3 +1,46 @@
</span><ins>+2014-02-07  Brady Eidson  &lt;beidson@apple.com&gt;
+
+        IDB: Some Mozilla cursor mutation tests fail
+        &lt;rdar://problem/16011680&gt; and https://bugs.webkit.org/show_bug.cgi?id=128374
+
+        Reviewed by Sam Weinig.
+
+        Tested by:
+        storage/indexeddb/mozilla/cursor-mutation-objectstore-only.html
+        storage/indexeddb/mozilla/cursor-mutation.html
+
+        The previous comment about LevelDB was wrong.
+        Apparently calling onSuccess() with a null SharedBuffer means the cursor reached the end.
+        Update to distinguish between an error and reaching the end:
+        * Modules/indexeddb/IDBCursorBackendOperations.cpp:
+        (WebCore::CursorAdvanceOperation::perform):
+        (WebCore::CursorIterationOperation::perform):

+        Add a new IDBKey type - Maximum - To be used in comparisons between keys.
+        This will allow the DatabaseProcess to operate on the range of all keys:
+        * Modules/indexeddb/IDBKey.cpp:
+        (WebCore::IDBKey::compare):
+        * Modules/indexeddb/IDBKey.h:
+
+        * Modules/indexeddb/IDBKeyData.cpp:
+        (WebCore::IDBKeyData::IDBKeyData):
+        (WebCore::IDBKeyData::maybeCreateIDBKey):
+        (WebCore::IDBKeyData::isolatedCopy):
+        (WebCore::IDBKeyData::encode):
+        (WebCore::IDBKeyData::decode):
+        (WebCore::IDBKeyData::compare):
+        (WebCore::IDBKeyData::loggingString):
+        * Modules/indexeddb/IDBKeyData.h:
+        (WebCore::IDBKeyData::minimum):
+        (WebCore::IDBKeyData::maximum):
+
+        * bindings/js/IDBBindingUtilities.cpp:
+        (WebCore::idbKeyToJSValue):
+
+        Remove an unused callback:
+        * Modules/indexeddb/IDBCallbacks.h:
+        * Modules/indexeddb/IDBRequest.h:
+
</ins><span class="cx"> 2014-02-07  Roger Fong  &lt;roger_fong@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         CGContextGetUserSpaceToDeviceSpaceTransform returns the wrong value on Windows.
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBCallbacksh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBCallbacks.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBCallbacks.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBCallbacks.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -68,8 +68,6 @@
</span><span class="cx">     // From IDBCursor.advance()/continue()
</span><span class="cx">     virtual void onSuccess(PassRefPtr&lt;IDBKey&gt;, PassRefPtr&lt;IDBKey&gt; primaryKey, PassRefPtr&lt;SharedBuffer&gt;) = 0;
</span><span class="cx"> 
</span><del>-    // From IDBCursor.advance()/continue()
-    virtual void onSuccessWithPrefetch(const Vector&lt;RefPtr&lt;IDBKey&gt;&gt;&amp; keys, const Vector&lt;RefPtr&lt;IDBKey&gt;&gt;&amp; primaryKeys, const Vector&lt;RefPtr&lt;SharedBuffer&gt;&gt;&amp; values) = 0;
</del><span class="cx">     // From IDBFactory.open()/deleteDatabase()
</span><span class="cx">     virtual void onBlocked(uint64_t /* existingVersion */) { ASSERT_NOT_REACHED(); }
</span><span class="cx">     // From IDBFactory.open()
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBCursorBackendOperationscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -43,8 +43,10 @@
</span><span class="cx">     auto callback = [this, operation, completionCallback](PassRefPtr&lt;IDBKey&gt; key, PassRefPtr&lt;IDBKey&gt; primaryKey, PassRefPtr&lt;SharedBuffer&gt; valueBuffer, PassRefPtr&lt;IDBDatabaseError&gt; error) {
</span><span class="cx">         if (error) {
</span><span class="cx">             m_cursor-&gt;clear();
</span><del>-            // FIXME: The LevelDB backend calls onSuccess even on failure.
-            // This will probably have to change soon (for sanity) and will probably break LevelDB
</del><ins>+            m_callbacks-&gt;onError(error);
+        } else if (!key) {
+            // If there's no error but also no key, then the cursor reached the end.
+            m_cursor-&gt;clear();
</ins><span class="cx">             m_callbacks-&gt;onSuccess(static_cast&lt;SharedBuffer*&gt;(0));
</span><span class="cx">         } else {
</span><span class="cx">             m_cursor-&gt;updateCursorData(key.get(), primaryKey.get(), valueBuffer.get());
</span><span class="lines">@@ -66,8 +68,10 @@
</span><span class="cx">     auto callback = [this, operation, completionCallback](PassRefPtr&lt;IDBKey&gt; key, PassRefPtr&lt;IDBKey&gt; primaryKey, PassRefPtr&lt;SharedBuffer&gt; valueBuffer, PassRefPtr&lt;IDBDatabaseError&gt; error) {
</span><span class="cx">         if (error) {
</span><span class="cx">             m_cursor-&gt;clear();
</span><del>-            // FIXME: The LevelDB backend calls onSuccess even on failure.
-            // This will probably have to change soon (for sanity) and will probably break LevelDB
</del><ins>+            m_callbacks-&gt;onError(error);
+        } else if (!key) {
+            // If there's no error but also no key, then the cursor reached the end.
+            m_cursor-&gt;clear();
</ins><span class="cx">             m_callbacks-&gt;onSuccess(static_cast&lt;SharedBuffer*&gt;(0));
</span><span class="cx">         } else {
</span><span class="cx">             m_cursor-&gt;updateCursorData(key.get(), primaryKey.get(), valueBuffer.get());
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBKeycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBKey.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBKey.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBKey.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -76,6 +76,7 @@
</span><span class="cx">                 (m_number &gt; other-&gt; m_number) ? 1 : 0;
</span><span class="cx">     case InvalidType:
</span><span class="cx">     case MinType:
</span><ins>+    case MaxType:
</ins><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return 0;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBKeyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBKey.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBKey.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBKey.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -98,6 +98,7 @@
</span><span class="cx"> 
</span><span class="cx">     // In order of the least to the highest precedent in terms of sort order.
</span><span class="cx">     enum Type {
</span><ins>+        MaxType = -1,
</ins><span class="cx">         InvalidType = 0,
</span><span class="cx">         ArrayType,
</span><span class="cx">         StringType,
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBKeyDatacpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -61,8 +61,8 @@
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         numberValue = key-&gt;number();
</span><span class="cx">         break;
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><del>-        ASSERT_NOT_REACHED();
</del><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="lines">@@ -90,6 +90,7 @@
</span><span class="cx">         return IDBKey::createDate(numberValue);
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         return IDBKey::createNumber(numberValue);
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return nullptr;
</span><span class="lines">@@ -119,8 +120,8 @@
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         result.numberValue = numberValue;
</span><span class="cx">         return result;
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><del>-        ASSERT_NOT_REACHED();
</del><span class="cx">         return result;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -151,8 +152,8 @@
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         encoder.encodeDouble(&quot;number&quot;, numberValue);
</span><span class="cx">         return;
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><del>-        ASSERT_NOT_REACHED();
</del><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -168,7 +169,8 @@
</span><span class="cx">         return true;
</span><span class="cx"> 
</span><span class="cx">     auto enumFunction = [](int64_t value) {
</span><del>-        return value == IDBKey::InvalidType
</del><ins>+        return value == IDBKey::MaxType
+            || value == IDBKey::InvalidType
</ins><span class="cx">             || value == IDBKey::ArrayType
</span><span class="cx">             || value == IDBKey::StringType
</span><span class="cx">             || value == IDBKey::DateType
</span><span class="lines">@@ -181,11 +183,12 @@
</span><span class="cx">     if (result.type == IDBKey::InvalidType)
</span><span class="cx">         return true;
</span><span class="cx"> 
</span><del>-    if (result.type == IDBKey::MinType) {
-        ASSERT_NOT_REACHED();
</del><ins>+    if (result.type == IDBKey::MaxType)
</ins><span class="cx">         return true;
</span><del>-    }
</del><span class="cx"> 
</span><ins>+    if (result.type == IDBKey::MinType)
+        return true;
+
</ins><span class="cx">     if (result.type == IDBKey::StringType)
</span><span class="cx">         return decoder.decodeString(&quot;string&quot;, result.stringValue);
</span><span class="cx"> 
</span><span class="lines">@@ -237,8 +240,8 @@
</span><span class="cx">         if (numberValue == other.numberValue)
</span><span class="cx">             return 0;
</span><span class="cx">         return numberValue &gt; other.numberValue ? 1 : -1;
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><del>-        ASSERT_NOT_REACHED();
</del><span class="cx">         return 0;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -272,6 +275,8 @@
</span><span class="cx">         return String::format(&quot;Date type - %f&quot;, numberValue);
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         return String::format(&quot;&lt;number&gt; - %f&quot;, numberValue);
</span><ins>+    case IDBKey::MaxType:
+        return &quot;&lt;maximum&gt;&quot;;
</ins><span class="cx">     case IDBKey::MinType:
</span><span class="cx">         return &quot;&lt;minimum&gt;&quot;;
</span><span class="cx">     default:
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBKeyDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -45,6 +45,22 @@
</span><span class="cx"> 
</span><span class="cx">     IDBKeyData(const IDBKey*);
</span><span class="cx"> 
</span><ins>+    static IDBKeyData minimum()
+    {
+        IDBKeyData result;
+        result.type = IDBKey::MinType;
+        result.isNull = false;
+        return result;
+    }
+
+    static IDBKeyData maximum()
+    {
+        IDBKeyData result;
+        result.type = IDBKey::MaxType;
+        result.isNull = false;
+        return result;
+    }
+
</ins><span class="cx">     PassRefPtr&lt;IDBKey&gt; maybeCreateIDBKey() const;
</span><span class="cx"> 
</span><span class="cx">     IDBKeyData isolatedCopy() const;
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBRequesth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBRequest.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -93,7 +93,6 @@
</span><span class="cx">     virtual void onSuccess(int64_t);
</span><span class="cx">     virtual void onSuccess();
</span><span class="cx">     virtual void onSuccess(PassRefPtr&lt;IDBKey&gt;, PassRefPtr&lt;IDBKey&gt; primaryKey, PassRefPtr&lt;SharedBuffer&gt;);
</span><del>-    virtual void onSuccessWithPrefetch(const Vector&lt;RefPtr&lt;IDBKey&gt;&gt;&amp;, const Vector&lt;RefPtr&lt;IDBKey&gt;&gt;&amp;, const Vector&lt;RefPtr&lt;SharedBuffer&gt;&gt;&amp;) { ASSERT_NOT_REACHED(); } // Not implemented. Callback should not reach the renderer side.
</del><span class="cx"> 
</span><span class="cx">     // ActiveDOMObject
</span><span class="cx">     virtual bool hasPendingActivity() const override;
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsIDBBindingUtilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -101,6 +101,7 @@
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         return jsNumber(key-&gt;number());
</span><span class="cx">     case IDBKey::MinType:
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::InvalidType:
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return jsUndefined();
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/ChangeLog        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -1,3 +1,57 @@
</span><ins>+2014-02-07  Brady Eidson  &lt;beidson@apple.com&gt;
+
+        IDB: Some Mozilla cursor mutation tests fail
+        &lt;rdar://problem/16011680&gt; and https://bugs.webkit.org/show_bug.cgi?id=128374
+
+        Reviewed by Sam Weinig.
+
+        After a SQLite statement is prepared, stepping it will not pick up any subsequent changes
+        to the object store.
+
+        By keeping track of the current record we’re looking at in the object store and being told
+        that the object store changed its contents, we can reset the statement to pick up where it
+        left off but with the new object store contents.
+
+        * DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp:
+        (WebKit::UniqueIDBDatabase::putRecordInBackingStore): Tell the backing store to notify
+          its cursors that the object store changed its records.
+        (WebKit::UniqueIDBDatabase::deleteRangeInBackingStore): Ditto.
+
+        * DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h:
+        * DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp:
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::notifyCursorsOfChanges): Tell the transaction
+          to notify its cursors that the object store changed its records.
+        * DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h:
+
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp:
+        (WebKit::SQLiteIDBTransaction::notifyCursorsOfChanges): Tell the relevant cursors that their
+          object store changed its records.
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h:
+
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp:
+        (WebKit::SQLiteIDBCursor::SQLiteIDBCursor):
+        (WebKit::getIndexStatement): All statements now have a lower and upper range. By default we bind
+          IDBKey::Minimum to the lower range and IDBKey::Maximum to the upper range.
+        (WebKit::getObjectStoreStatement): Ditto.
+        (WebKit::SQLiteIDBCursor::establishStatement):
+        (WebKit::SQLiteIDBCursor::createSQLiteStatement):
+        (WebKit::SQLiteIDBCursor::objectStoreRecordsChanged): Set the flag indicating the statement
+          needs to be reset and rebound.
+        (WebKit::SQLiteIDBCursor::resetAndRebindStatement):
+        (WebKit::SQLiteIDBCursor::bindArguments): Factored out to be shared between statement prepare()
+          and statement reset().
+        (WebKit::SQLiteIDBCursor::advance):
+        (WebKit::SQLiteIDBCursor::advanceOnce): If the statement needs to be reset, do so before advancing it.
+        (WebKit::SQLiteIDBCursor::internalAdvanceOnce): In a few cases, tell advanceOnce that it needs to
+          try again because it is on a missing record or is repeating a record.
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h:
+        (WebKit::SQLiteIDBCursor::objectStoreID):
+
+        Make sure Maximum and Minimum keys are never sent across IPC, as that doesn’t make sense:
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder&lt;IDBKeyData&gt;::encode):
+        (IPC::ArgumentCoder&lt;IDBKeyData&gt;::decode):
+
</ins><span class="cx"> 2014-02-07  Anders Carlsson  &lt;andersca@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Make it possible for each web page to have different preferences
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBUniqueIDBDatabasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -866,6 +866,8 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    m_backingStore-&gt;notifyCursorsOfChanges(transaction, objectStoreMetadata.id);
+
</ins><span class="cx">     if (putMode != IDBDatabaseBackend::CursorUpdate &amp;&amp; objectStoreMetadata.autoIncrement &amp;&amp; key.type == IDBKey::NumberType) {
</span><span class="cx">         if (!m_backingStore-&gt;updateKeyGeneratorNumber(transaction, objectStoreMetadata.id, keyNumber, keyWasGenerated)) {
</span><span class="cx">             postMainThreadTask(createAsyncTask(*this, &amp;UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Internal backing store error updating key generator&quot;)));
</span><span class="lines">@@ -1049,6 +1051,8 @@
</span><span class="cx">         postMainThreadTask(createAsyncTask(*this, &amp;UniqueIDBDatabase::didDeleteRangeInBackingStore, requestID, IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to get count from backing store&quot;)));
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    m_backingStore-&gt;notifyCursorsOfChanges(transactionIdentifier, objectStoreID);
+
</ins><span class="cx">     postMainThreadTask(createAsyncTask(*this, &amp;UniqueIDBDatabase::didDeleteRangeInBackingStore, requestID, 0, String(StringImpl::empty())));
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBUniqueIDBDatabaseBackingStoreh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx">     virtual bool openCursor(const IDBIdentifier&amp; transactionIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&amp;, int64_t&amp; cursorID, WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, Vector&lt;uint8_t&gt;&amp;) = 0;
</span><span class="cx">     virtual bool advanceCursor(const IDBIdentifier&amp; cursorIdentifier, uint64_t count, WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, Vector&lt;uint8_t&gt;&amp;) = 0;
</span><span class="cx">     virtual bool iterateCursor(const IDBIdentifier&amp; cursorIdentifier, const WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, Vector&lt;uint8_t&gt;&amp;) = 0;
</span><ins>+    virtual void notifyCursorsOfChanges(const IDBIdentifier&amp; transactionIdentifier, int64_t objectStoreID) = 0;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBCursorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -58,6 +58,9 @@
</span><span class="cx">     , m_indexID(indexID)
</span><span class="cx">     , m_cursorDirection(cursorDirection)
</span><span class="cx">     , m_keyRange(keyRange)
</span><ins>+    , m_currentRecordID(-1)
+    , m_statementNeedsReset(false)
+    , m_boundID(0)
</ins><span class="cx">     , m_completed(false)
</span><span class="cx">     , m_errored(false)
</span><span class="cx"> {
</span><span class="lines">@@ -69,46 +72,32 @@
</span><span class="cx">     DEFINE_STATIC_LOCAL(Vector&lt;String&gt;, indexStatements, ());
</span><span class="cx"> 
</span><span class="cx">     if (indexStatements.isEmpty()) {
</span><del>-        indexStatements.reserveCapacity(18);
</del><ins>+        indexStatements.reserveCapacity(8);
</ins><span class="cx"> 
</span><del>-        // No lower key statements (6)
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</del><ins>+        // Lower missing/open, upper missing/open.
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</ins><span class="cx"> 
</span><del>-        // Closed lower key statements (6)
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</del><ins>+        // Lower missing/open, upper closed.
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</ins><span class="cx"> 
</span><del>-        // Open lower key statements (6)
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM IndexRecords WHERE indexID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</del><ins>+        // Lower closed, upper missing/open.
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
+
+        // Lower closed, upper closed.
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
+        indexStatements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     size_t i = 0;
</span><span class="cx"> 
</span><del>-    if (hasLowerKey) {
-        i += 6;
-        if (isLowerOpen)
-            i += 6;
-    }
</del><ins>+    if (hasLowerKey &amp;&amp; !isLowerOpen)
+        i += 4;
</ins><span class="cx"> 
</span><del>-    if (hasUpperKey) {
</del><ins>+    if (hasUpperKey &amp;&amp; !isUpperOpen)
</ins><span class="cx">         i += 2;
</span><del>-        if (isUpperOpen)
-            i += 2;
-    }
</del><span class="cx"> 
</span><span class="cx">     if (descending)
</span><span class="cx">         i += 1;
</span><span class="lines">@@ -118,104 +107,135 @@
</span><span class="cx"> 
</span><span class="cx"> static const String&amp; getObjectStoreStatement(bool hasLowerKey, bool isLowerOpen, bool hasUpperKey, bool isUpperOpen, bool descending)
</span><span class="cx"> {
</span><del>-    DEFINE_STATIC_LOCAL(Vector&lt;String&gt;, indexStatements, ());
</del><ins>+    DEFINE_STATIC_LOCAL(Vector&lt;String&gt;, statements, ());
</ins><span class="cx"> 
</span><del>-    if (indexStatements.isEmpty()) {
-        indexStatements.reserveCapacity(18);
</del><ins>+    if (statements.isEmpty()) {
+        statements.reserveCapacity(8);
</ins><span class="cx"> 
</span><del>-        // No lower key statements (6)
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</del><ins>+        // Lower missing/open, upper missing/open.
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</ins><span class="cx"> 
</span><del>-        // Closed lower key statements (6)
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</del><ins>+        // Lower missing/open, upper closed.
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</ins><span class="cx"> 
</span><del>-        // Open lower key statements (6)
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
-        indexStatements.append(ASCIILiteral(&quot;SELECT key, value FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</del><ins>+        // Lower closed, upper missing/open.
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;));
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key DESC;&quot;));
+
+        // Lower closed, upper closed.
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;));
+        statements.append(ASCIILiteral(&quot;SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key DESC;&quot;));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     size_t i = 0;
</span><span class="cx"> 
</span><del>-    if (hasLowerKey) {
-        i += 6;
-        if (isLowerOpen)
-            i += 6;
-    }
</del><ins>+    if (hasLowerKey &amp;&amp; !isLowerOpen)
+        i += 4;
</ins><span class="cx"> 
</span><del>-    if (hasUpperKey) {
</del><ins>+    if (hasUpperKey &amp;&amp; !isUpperOpen)
</ins><span class="cx">         i += 2;
</span><del>-        if (isUpperOpen)
-            i += 2;
-    }
</del><span class="cx"> 
</span><span class="cx">     if (descending)
</span><span class="cx">         i += 1;
</span><span class="cx"> 
</span><del>-    return indexStatements[i];
</del><ins>+    return statements[i];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool SQLiteIDBCursor::establishStatement()
</span><span class="cx"> {
</span><ins>+    ASSERT(!m_statement);
</ins><span class="cx">     String sql;
</span><del>-    int64_t id;
</del><span class="cx"> 
</span><span class="cx">     if (m_indexID != IDBIndexMetadata::InvalidId) {
</span><span class="cx">         sql = getIndexStatement(!m_keyRange.lowerKey.isNull, m_keyRange.lowerOpen, !m_keyRange.upperKey.isNull, m_keyRange.upperOpen, m_cursorDirection == IndexedDB::CursorDirection::Prev || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate);
</span><del>-        id = m_indexID;
</del><ins>+        m_boundID = m_indexID;
</ins><span class="cx">     } else {
</span><span class="cx">         sql = getObjectStoreStatement(!m_keyRange.lowerKey.isNull, m_keyRange.lowerOpen, !m_keyRange.upperKey.isNull, m_keyRange.upperOpen, m_cursorDirection == IndexedDB::CursorDirection::Prev || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate);
</span><del>-        id = m_objectStoreID;
</del><ins>+        m_boundID = m_objectStoreID;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return createSQLiteStatement(sql, id);
</del><ins>+    m_currentLowerKey = m_keyRange.lowerKey.isNull ? IDBKeyData::minimum() : m_keyRange.lowerKey;
+    m_currentUpperKey = m_keyRange.upperKey.isNull ? IDBKeyData::maximum() : m_keyRange.upperKey;
+
+    return createSQLiteStatement(sql);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool SQLiteIDBCursor::createSQLiteStatement(const String&amp; sql, int64_t idToBind)
</del><ins>+bool SQLiteIDBCursor::createSQLiteStatement(const String&amp; sql)
</ins><span class="cx"> {
</span><ins>+    LOG(IDB, &quot;Creating cursor with SQL query: \&quot;%s\&quot;&quot;, sql.utf8().data());
+
+    ASSERT(!m_currentLowerKey.isNull);
+    ASSERT(!m_currentUpperKey.isNull);
</ins><span class="cx">     ASSERT(m_transaction-&gt;sqliteTransaction());
</span><del>-    SQLiteDatabase&amp; database = m_transaction-&gt;sqliteTransaction()-&gt;database();
</del><span class="cx"> 
</span><del>-    LOG(IDB, &quot;Creating cursor with SQL query: \&quot;%s\&quot;&quot;, sql.utf8().data());
</del><ins>+    m_statement = std::make_unique&lt;SQLiteStatement&gt;(m_transaction-&gt;sqliteTransaction()-&gt;database(), sql);
</ins><span class="cx"> 
</span><del>-    m_statement = std::make_unique&lt;SQLiteStatement&gt;(database, sql);
-
-    if (m_statement-&gt;prepare() != SQLResultOk
-        || m_statement-&gt;bindInt64(1, idToBind) != SQLResultOk) {
-        LOG_ERROR(&quot;Could not create cursor statement&quot;);
</del><ins>+    if (m_statement-&gt;prepare() != SQLResultOk) {
+        LOG_ERROR(&quot;Could not create cursor statement (prepare/id) - '%s'&quot;, m_transaction-&gt;sqliteTransaction()-&gt;database().lastErrorMsg());
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    int nextBindArgument = 2;
</del><ins>+    return bindArguments();
+}
</ins><span class="cx"> 
</span><del>-    if (!m_keyRange.lowerKey.isNull) {
-        RefPtr&lt;SharedBuffer&gt; buffer = serializeIDBKeyData(m_keyRange.lowerKey);
-        if (m_statement-&gt;bindBlob(nextBindArgument++, buffer-&gt;data(), buffer-&gt;size()) != SQLResultOk) {
-            LOG_ERROR(&quot;Could not create cursor statement&quot;);
-            return false;
-        }
</del><ins>+void SQLiteIDBCursor::objectStoreRecordsChanged()
+{
+    // If ObjectStore or Index contents changed, we need to reset the statement and bind new parameters to it.
+    // This is to pick up any changes that might exist.
+
+    m_statementNeedsReset = true;
+}
+
+void SQLiteIDBCursor::resetAndRebindStatement()
+{
+    ASSERT(!m_currentLowerKey.isNull);
+    ASSERT(!m_currentUpperKey.isNull);
+    ASSERT(m_transaction-&gt;sqliteTransaction());
+    ASSERT(m_statement);
+    ASSERT(m_statementNeedsReset);
+
+    m_statementNeedsReset = false;
+
+    // If this cursor never fetched any records, we don't need to reset the statement.
+    if (m_currentKey.isNull)
+        return;
+
+    // Otherwise update the lower key or upper key used for the cursor range.
+    // This is so the cursor can pick up where we left off.
+    if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate)
+        m_currentLowerKey = m_currentKey;
+    else
+        m_currentUpperKey = m_currentKey;
+
+    if (m_statement-&gt;reset() != SQLResultOk) {
+        LOG_ERROR(&quot;Could not reset cursor statement to respond to object store changes&quot;);
+        return;
</ins><span class="cx">     }
</span><del>-    if (!m_keyRange.upperKey.isNull) {
-        RefPtr&lt;SharedBuffer&gt; buffer = serializeIDBKeyData(m_keyRange.upperKey);
-        if (m_statement-&gt;bindBlob(nextBindArgument, buffer-&gt;data(), buffer-&gt;size()) != SQLResultOk) {
-            LOG_ERROR(&quot;Could not create cursor statement&quot;);
-            return false;
-        }
</del><ins>+
+    bindArguments();
+}
+
+bool SQLiteIDBCursor::bindArguments()
+{
+    if (m_statement-&gt;bindInt64(1, m_boundID) != SQLResultOk) {
+        LOG_ERROR(&quot;Could not bind id argument (bound ID)&quot;);
+        return false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    RefPtr&lt;SharedBuffer&gt; buffer = serializeIDBKeyData(m_currentLowerKey);
+    if (m_statement-&gt;bindBlob(2, buffer-&gt;data(), buffer-&gt;size()) != SQLResultOk) {
+        LOG_ERROR(&quot;Could not create cursor statement (lower key)&quot;);
+        return false;
+    }
+
+    buffer = serializeIDBKeyData(m_currentUpperKey);
+    if (m_statement-&gt;bindBlob(3, buffer-&gt;data(), buffer-&gt;size()) != SQLResultOk) {
+        LOG_ERROR(&quot;Could not create cursor statement (upper key)&quot;);
+        return false;
+    }
+
</ins><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -227,11 +247,10 @@
</span><span class="cx">         if (!isUnique) {
</span><span class="cx">             if (!advanceOnce())
</span><span class="cx">                 return false;
</span><del>-            continue;
</del><ins>+        } else {
+            if (!advanceUnique())
+                return false;
</ins><span class="cx">         }
</span><del>-
-        if (!advanceUnique())
-            return false;
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return true;
</span><span class="lines">@@ -253,15 +272,27 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> bool SQLiteIDBCursor::advanceOnce()
</span><span class="cx"> {
</span><ins>+    if (m_statementNeedsReset)
+        resetAndRebindStatement();
+
+    AdvanceResult result;
+    do {
+        result = internalAdvanceOnce();
+    } while (result == AdvanceResult::ShouldAdvanceAgain);
+
+    return result == AdvanceResult::Success;
+}
+
+SQLiteIDBCursor::AdvanceResult SQLiteIDBCursor::internalAdvanceOnce()
+{
</ins><span class="cx">     ASSERT(m_transaction-&gt;sqliteTransaction());
</span><span class="cx">     ASSERT(m_statement);
</span><span class="cx"> 
</span><span class="cx">     if (m_completed) {
</span><span class="cx">         LOG_ERROR(&quot;Attempt to advance a completed cursor&quot;);
</span><del>-        return false;
</del><ins>+        return AdvanceResult::Failure;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     int result = m_statement-&gt;step();
</span><span class="lines">@@ -273,27 +304,37 @@
</span><span class="cx">         m_currentPrimaryKey = IDBKeyData();
</span><span class="cx">         m_currentValueBuffer.clear();
</span><span class="cx"> 
</span><del>-        return true;
</del><ins>+        return AdvanceResult::Success;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (result != SQLResultRow) {
</span><span class="cx">         LOG_ERROR(&quot;Error advancing cursor - (%i) %s&quot;, result, m_transaction-&gt;sqliteTransaction()-&gt;database().lastErrorMsg());
</span><span class="cx">         m_completed = true;
</span><span class="cx">         m_errored = true;
</span><del>-        return false;
</del><ins>+        return AdvanceResult::Failure;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    int64_t recordID = m_statement-&gt;getColumnInt64(0);
+
+    // If the recordID of the record just fetched is the same as the current record ID
+    // then this statement must have been re-prepared in response to an object store change.
+    // We don't want to re-use the current record so we'll move on to the next one.
+    if (recordID == m_currentRecordID)
+        return AdvanceResult::ShouldAdvanceAgain;
+
+    m_currentRecordID = recordID;
+
</ins><span class="cx">     Vector&lt;uint8_t&gt; keyData;
</span><del>-    m_statement-&gt;getColumnBlobAsVector(0, keyData);
</del><ins>+    m_statement-&gt;getColumnBlobAsVector(1, keyData);
</ins><span class="cx"> 
</span><span class="cx">     if (!deserializeIDBKeyData(keyData.data(), keyData.size(), m_currentKey)) {
</span><span class="cx">         LOG_ERROR(&quot;Unable to deserialize key data from database while advancing cursor&quot;);
</span><span class="cx">         m_completed = true;
</span><span class="cx">         m_errored = true;
</span><del>-        return false;
</del><ins>+        return AdvanceResult::Failure;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    m_statement-&gt;getColumnBlobAsVector(1, keyData);
</del><ins>+    m_statement-&gt;getColumnBlobAsVector(2, keyData);
</ins><span class="cx">     m_currentValueBuffer = keyData;
</span><span class="cx"> 
</span><span class="cx">     if (m_indexID != IDBIndexMetadata::InvalidId) {
</span><span class="lines">@@ -301,25 +342,38 @@
</span><span class="cx">             LOG_ERROR(&quot;Unable to deserialize value data from database while advancing index cursor&quot;);
</span><span class="cx">             m_completed = true;
</span><span class="cx">             m_errored = true;
</span><del>-            return false;
</del><ins>+            return AdvanceResult::Failure;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         SQLiteStatement objectStoreStatement(*m_statement-&gt;database(), &quot;SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;&quot;);
</span><span class="cx"> 
</span><span class="cx">         if (objectStoreStatement.prepare() != SQLResultOk
</span><span class="cx">             || objectStoreStatement.bindBlob(1, m_currentValueBuffer.data(), m_currentValueBuffer.size()) != SQLResultOk
</span><del>-            || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLResultOk
-            || objectStoreStatement.step() != SQLResultRow) {
-            LOG_ERROR(&quot;Could not create index cursor statement into object store records&quot;);
</del><ins>+            || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLResultOk) {
+            LOG_ERROR(&quot;Could not create index cursor statement into object store records (%i) '%s'&quot;, m_statement-&gt;database()-&gt;lastError(), m_statement-&gt;database()-&gt;lastErrorMsg());
</ins><span class="cx">             m_completed = true;
</span><span class="cx">             m_errored = true;
</span><del>-            return false;
</del><ins>+            return AdvanceResult::Failure;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        objectStoreStatement.getColumnBlobAsVector(0, m_currentValueBuffer);
</del><ins>+        int result = objectStoreStatement.step();
+
+        if (result == SQLResultRow)
+            objectStoreStatement.getColumnBlobAsVector(0, m_currentValueBuffer);
+        else if (result == SQLResultDone) {
+            // This indicates that the record we're trying to retrieve has been removed from the object store.
+            // Skip over it.
+            return AdvanceResult::ShouldAdvanceAgain;
+        } else {
+            LOG_ERROR(&quot;Could not step index cursor statement into object store records (%i) '%s'&quot;, m_statement-&gt;database()-&gt;lastError(), m_statement-&gt;database()-&gt;lastErrorMsg());
+            m_completed = true;
+            m_errored = true;
+            return AdvanceResult::Failure;
+
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return true;
</del><ins>+    return AdvanceResult::Success;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool SQLiteIDBCursor::iterate(const WebCore::IDBKeyData&amp; targetKey)
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBCursorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -56,6 +56,8 @@
</span><span class="cx">     const IDBIdentifier&amp; identifier() const { return m_cursorIdentifier; }
</span><span class="cx">     SQLiteIDBTransaction* transaction() const { return m_transaction; }
</span><span class="cx"> 
</span><ins>+    int64_t objectStoreID() const { return m_objectStoreID; }
+
</ins><span class="cx">     const WebCore::IDBKeyData&amp; currentKey() const { return m_currentKey; }
</span><span class="cx">     const WebCore::IDBKeyData&amp; currentPrimaryKey() const { return m_currentPrimaryKey; }
</span><span class="cx">     const Vector&lt;uint8_t&gt;&amp; currentValueBuffer() const { return m_currentValueBuffer; }
</span><span class="lines">@@ -65,12 +67,24 @@
</span><span class="cx"> 
</span><span class="cx">     bool didError() const { return m_errored; }
</span><span class="cx"> 
</span><ins>+    void objectStoreRecordsChanged();
+
</ins><span class="cx"> private:
</span><span class="cx">     SQLiteIDBCursor(SQLiteIDBTransaction*, const IDBIdentifier&amp; cursorIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&amp;);
</span><span class="cx"> 
</span><span class="cx">     bool establishStatement();
</span><del>-    bool createSQLiteStatement(const String&amp; sql, int64_t idToBind);
</del><ins>+    bool createSQLiteStatement(const String&amp; sql);
+    bool bindArguments();
</ins><span class="cx"> 
</span><ins>+    void resetAndRebindStatement();
+
+    enum class AdvanceResult {
+        Success,
+        Failure,
+        ShouldAdvanceAgain
+    };
+
+    AdvanceResult internalAdvanceOnce();
</ins><span class="cx">     bool advanceOnce();
</span><span class="cx">     bool advanceUnique();
</span><span class="cx"> 
</span><span class="lines">@@ -81,11 +95,17 @@
</span><span class="cx">     WebCore::IndexedDB::CursorDirection m_cursorDirection;
</span><span class="cx">     WebCore::IDBKeyRangeData m_keyRange;
</span><span class="cx"> 
</span><ins>+    WebCore::IDBKeyData m_currentLowerKey;
+    WebCore::IDBKeyData m_currentUpperKey;
+
+    int64_t m_currentRecordID;
</ins><span class="cx">     WebCore::IDBKeyData m_currentKey;
</span><span class="cx">     WebCore::IDBKeyData m_currentPrimaryKey;
</span><span class="cx">     Vector&lt;uint8_t&gt; m_currentValueBuffer;
</span><span class="cx"> 
</span><span class="cx">     std::unique_ptr&lt;WebCore::SQLiteStatement&gt; m_statement;
</span><ins>+    bool m_statementNeedsReset;
+    int64_t m_boundID;
</ins><span class="cx"> 
</span><span class="cx">     bool m_completed;
</span><span class="cx">     bool m_errored;
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBTransactioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -123,6 +123,14 @@
</span><span class="cx">     m_cursors.remove(cursor.identifier());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void SQLiteIDBTransaction::notifyCursorsOfChanges(int64_t objectStoreID)
+{
+    for (auto&amp; i : m_cursors) {
+        if (i.value-&gt;objectStoreID() == objectStoreID)
+            i.value-&gt;objectStoreRecordsChanged();
+    }
+}
+
</ins><span class="cx"> void SQLiteIDBTransaction::clearCursors()
</span><span class="cx"> {
</span><span class="cx">     for (auto&amp; cursor : m_cursors.values())
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBsqliteSQLiteIDBTransactionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -73,6 +73,7 @@
</span><span class="cx">     SQLiteIDBCursor* openCursor(int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&amp;);
</span><span class="cx"> 
</span><span class="cx">     void closeCursor(SQLiteIDBCursor&amp;);
</span><ins>+    void notifyCursorsOfChanges(int64_t objectStoreID);
</ins><span class="cx"> 
</span><span class="cx">     WebCore::IndexedDB::TransactionMode mode() const { return m_mode; }
</span><span class="cx">     bool inProgress() const;
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBsqliteUniqueIDBDatabaseBackingStoreSQLitecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -1267,6 +1267,21 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void UniqueIDBDatabaseBackingStoreSQLite::notifyCursorsOfChanges(const IDBIdentifier&amp; transactionIdentifier, int64_t objectStoreID)
+{
+    ASSERT(!isMainThread());
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB-&gt;isOpen());
+
+    SQLiteIDBTransaction* transaction = m_transactions.get(transactionIdentifier);
+    if (!transaction || !transaction-&gt;inProgress()) {
+        LOG_ERROR(&quot;Attempt to notify cursors of changes in database without an established, in-progress transaction&quot;);
+        return;
+    }
+
+    transaction-&gt;notifyCursorsOfChanges(objectStoreID);
+}
+
</ins><span class="cx"> int UniqueIDBDatabaseBackingStoreSQLite::idbKeyCollate(int aLength, const void* aBuffer, int bLength, const void* bBuffer)
</span><span class="cx"> {
</span><span class="cx">     IDBKeyData a, b;
</span></span></pre></div>
<a id="trunkSourceWebKit2DatabaseProcessIndexedDBsqliteUniqueIDBDatabaseBackingStoreSQLiteh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -91,6 +91,7 @@
</span><span class="cx">     virtual bool openCursor(const IDBIdentifier&amp; transactionIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&amp;, int64_t&amp; cursorID, WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, Vector&lt;uint8_t&gt;&amp;) override;
</span><span class="cx">     virtual bool advanceCursor(const IDBIdentifier&amp; cursorIdentifier, uint64_t count, WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, Vector&lt;uint8_t&gt;&amp;) override;
</span><span class="cx">     virtual bool iterateCursor(const IDBIdentifier&amp; cursorIdentifier, const WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, WebCore::IDBKeyData&amp;, Vector&lt;uint8_t&gt;&amp;) override;
</span><ins>+    virtual void notifyCursorsOfChanges(const IDBIdentifier&amp; transactionIdentifier, int64_t objectStoreID) override;
</ins><span class="cx"> 
</span><span class="cx">     void unregisterCursor(SQLiteIDBCursor*);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedWebCoreArgumentCoderscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp (163661 => 163662)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp        2014-02-08 00:24:13 UTC (rev 163661)
+++ trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp        2014-02-08 00:30:50 UTC (rev 163662)
</span><span class="lines">@@ -1724,7 +1724,10 @@
</span><span class="cx">     case IDBKey::NumberType:
</span><span class="cx">         encoder &lt;&lt; keyData.numberValue;
</span><span class="cx">         break;
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><ins>+        // MaxType and MinType are only used for comparison to other keys.
+        // They should never be sent across the wire.
</ins><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         break;
</span><span class="cx">     }
</span><span class="lines">@@ -1757,7 +1760,10 @@
</span><span class="cx">         if (!decoder.decode(keyData.numberValue))
</span><span class="cx">             return false;
</span><span class="cx">         break;
</span><ins>+    case IDBKey::MaxType:
</ins><span class="cx">     case IDBKey::MinType:
</span><ins>+        // MaxType and MinType are only used for comparison to other keys.
+        // They should never be sent across the wire.
</ins><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return false;
</span><span class="cx">     }
</span></span></pre>
</div>
</div>

</body>
</html>