<!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>[208771] 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/208771">208771</a></dd>
<dt>Author</dt> <dd>beidson@apple.com</dd>
<dt>Date</dt> <dd>2016-11-15 16:25:28 -0800 (Tue, 15 Nov 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>IndexedDB 2.0: Key collation during SQLite lookups is insanely slow.
https://bugs.webkit.org/show_bug.cgi?id=164754

Reviewed by Alex Christensen.

Source/WebCore:

No new tests (Covered by *all* existing tests, and unskips a previously-too-slow test)

The new serialization format is straight forward enough to get back with minimal documentation
in a comment with the code itself being the rest of the documentation.

It handles all current IDB key types and leaves room for future key types.

* Modules/indexeddb/IDBKeyData.cpp:
(WebCore::IDBKeyData::setBinaryValue):
* Modules/indexeddb/IDBKeyData.h:
(WebCore::IDBKeyData::binary):

* Modules/indexeddb/server/IDBSerialization.cpp:
(WebCore::serializedTypeForKeyType):
(WebCore::writeLittleEndian):
(WebCore::readLittleEndian):
(WebCore::writeDouble):
(WebCore::readDouble):
(WebCore::encodeKey):
(WebCore::serializeIDBKeyData):
(WebCore::decodeKey):
(WebCore::deserializeIDBKeyData):
* Modules/indexeddb/server/IDBSerialization.h:

* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedPutIndexKey): Verify that Type == Invalid
  keys don't get into the database. This was happening before and the previous serialization
  supported it, but there's clearly no point in supporting it with the new serialization.

LayoutTests:

* TestExpectations: Unskip a test that passes even in debug builds, and re-classify
  a test that used to be too-slow everywhere to be too-slow only in debug builds.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsTestExpectations">trunk/LayoutTests/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</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="#trunkSourceWebCoreModulesindexeddbserverIDBSerializationcpp">trunk/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbserverSQLiteIDBBackingStorecpp">trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/LayoutTests/ChangeLog        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2016-11-15  Brady Eidson  &lt;beidson@apple.com&gt;
+
+        IndexedDB 2.0: Key collation during SQLite lookups is insanely slow.
+        https://bugs.webkit.org/show_bug.cgi?id=164754
+
+        Reviewed by Alex Christensen.
+
+        * TestExpectations: Unskip a test that passes even in debug builds, and re-classify
+          a test that used to be too-slow everywhere to be too-slow only in debug builds.
+
</ins><span class="cx"> 2016-11-15  Simon Fraser  &lt;simon.fraser@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         UIScriptController: script with no async tasks fails if an earlier script registered a callback
</span></span></pre></div>
<a id="trunkLayoutTestsTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/TestExpectations (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/TestExpectations        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/LayoutTests/TestExpectations        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -848,9 +848,8 @@
</span><span class="cx"> # It's unclear if we need to continue supporting this.
</span><span class="cx"> storage/indexeddb/properties-disabled-at-runtime.html [ Failure ]
</span><span class="cx"> 
</span><del>-# Completes with passing results, but too slowly for our test timeouts.
-imported/w3c/web-platform-tests/IndexedDB/idbindex-multientry-big.htm [ Failure ]
-imported/w3c/web-platform-tests/IndexedDB/idbcursor_iterating.htm [ Failure ]
</del><ins>+# In debug builds, runs too slowly for the test timeout.
+[ Debug ] imported/w3c/web-platform-tests/IndexedDB/idbindex-multientry-big.htm [ Failure ]
</ins><span class="cx"> 
</span><span class="cx"> # SQLite backend tests that timeout
</span><span class="cx"> storage/indexeddb/modern/transaction-scheduler-1.html [ Skip ]
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/Source/WebCore/ChangeLog        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -1,3 +1,39 @@
</span><ins>+2016-11-15  Brady Eidson  &lt;beidson@apple.com&gt;
+
+        IndexedDB 2.0: Key collation during SQLite lookups is insanely slow.
+        https://bugs.webkit.org/show_bug.cgi?id=164754
+
+        Reviewed by Alex Christensen.
+
+        No new tests (Covered by *all* existing tests, and unskips a previously-too-slow test)
+
+        The new serialization format is straight forward enough to get back with minimal documentation
+        in a comment with the code itself being the rest of the documentation.
+        
+        It handles all current IDB key types and leaves room for future key types.
+
+        * Modules/indexeddb/IDBKeyData.cpp:
+        (WebCore::IDBKeyData::setBinaryValue):
+        * Modules/indexeddb/IDBKeyData.h:
+        (WebCore::IDBKeyData::binary):
+        
+        * Modules/indexeddb/server/IDBSerialization.cpp:
+        (WebCore::serializedTypeForKeyType):
+        (WebCore::writeLittleEndian):
+        (WebCore::readLittleEndian):
+        (WebCore::writeDouble):
+        (WebCore::readDouble):
+        (WebCore::encodeKey):
+        (WebCore::serializeIDBKeyData):
+        (WebCore::decodeKey):
+        (WebCore::deserializeIDBKeyData):
+        * Modules/indexeddb/server/IDBSerialization.h:
+        
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedPutIndexKey): Verify that Type == Invalid 
+          keys don't get into the database. This was happening before and the previous serialization
+          supported it, but there's clearly no point in supporting it with the new serialization.
+
</ins><span class="cx"> 2016-11-15  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Ensure sufficient buffer for worst-case URL encoding
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBKeyDatacpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -386,6 +386,14 @@
</span><span class="cx">     m_isNull = false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void IDBKeyData::setBinaryValue(const ThreadSafeDataBuffer&amp; value)
+{
+    *this = IDBKeyData();
+    m_value = value;
+    m_type = KeyType::Binary;
+    m_isNull = false;
+}
+
</ins><span class="cx"> void IDBKeyData::setStringValue(const String&amp; value)
</span><span class="cx"> {
</span><span class="cx">     *this = IDBKeyData();
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbIDBKeyDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBKeyData.h        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -79,6 +79,7 @@
</span><span class="cx">     WEBCORE_EXPORT int compare(const IDBKeyData&amp; other) const;
</span><span class="cx"> 
</span><span class="cx">     void setArrayValue(const Vector&lt;IDBKeyData&gt;&amp;);
</span><ins>+    void setBinaryValue(const ThreadSafeDataBuffer&amp;);
</ins><span class="cx">     void setStringValue(const String&amp;);
</span><span class="cx">     void setDateValue(double);
</span><span class="cx">     WEBCORE_EXPORT void setNumberValue(double);
</span><span class="lines">@@ -172,6 +173,12 @@
</span><span class="cx">         return WTF::get&lt;double&gt;(m_value);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    const ThreadSafeDataBuffer&amp; binary() const
+    {
+        ASSERT(m_type == KeyType::Binary);
+        return WTF::get&lt;ThreadSafeDataBuffer&gt;(m_value);
+    }
+
</ins><span class="cx">     const Vector&lt;IDBKeyData&gt;&amp; array() const
</span><span class="cx">     {
</span><span class="cx">         ASSERT(m_type == KeyType::Array);
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbserverIDBSerializationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -94,20 +94,322 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// This is the magic character that begins serialized PropertyLists, and tells us whether
+// the key we're looking at is an old-style key.
+static const uint8_t LegacySerializedKeyVersion = 'b';
+
+// FIXME: Linux ports uses KeyedEncoderGlib for their IDBKeys.
+// When a Glib maintainer comes along to enable the new serialization they'll need to
+// denote a Glib magic character here.
+
+/*
+The IDBKeyData serialization format is as follows:
+[1 byte version header][Key Buffer]
+
+The Key Buffer serialization format is as follows:
+[1 byte key type][Type specific data]
+
+Type specific serialization formats are as follows for each of the types:
+Min:
+[0 bytes]
+
+Number:
+[8 bytes representing a double encoded in little endian]
+
+Date:
+[8 bytes representing a double encoded in little endian]
+
+String:
+[4 bytes representing string &quot;length&quot; in little endian][&quot;length&quot; number of 2-byte pairs representing ECMAScript 16-bit code units]
+
+Binary:
+[8 bytes representing the &quot;size&quot; of the binary blob][&quot;size&quot; bytes]
+
+Array:
+[8 bytes representing the &quot;length&quot; of the key array][&quot;length&quot; individual Key Buffer entries]
+
+Max:
+[0 bytes]
+*/
+
+// FIXME: If the GLib magic character ends up being 0x00, we should consider changing
+// this 0x00 so we can support Glib keyed encoding, also.
+static const uint8_t SIDBKeyVersion = 0x00;
+enum class SIDBKeyType : uint8_t {
+    Min = 0x00,
+    Number = 0x20,
+    Date = 0x40,
+    String = 0x60,
+    Binary = 0x80,
+    Array = 0xA0,
+    Max = 0xFF,
+};
+
+static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type)
+{
+    switch (type) {
+    case IndexedDB::KeyType::Min:
+        return SIDBKeyType::Min;
+    case IndexedDB::KeyType::Number:
+        return SIDBKeyType::Number;
+    case IndexedDB::KeyType::Date:
+        return SIDBKeyType::Date;
+    case IndexedDB::KeyType::String:
+        return SIDBKeyType::String;
+    case IndexedDB::KeyType::Binary:
+        return SIDBKeyType::Binary;
+    case IndexedDB::KeyType::Array:
+        return SIDBKeyType::Array;
+    case IndexedDB::KeyType::Max:
+        return SIDBKeyType::Max;
+    case IndexedDB::KeyType::Invalid:
+        RELEASE_ASSERT_NOT_REACHED();
+    };
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
+template &lt;typename T&gt; static void writeLittleEndian(Vector&lt;char&gt;&amp; buffer, T value)
+{
+    for (unsigned i = 0; i &lt; sizeof(T); i++) {
+        buffer.append(value &amp; 0xFF);
+        value &gt;&gt;= 8;
+    }
+}
+
+template &lt;typename T&gt; static bool readLittleEndian(const uint8_t*&amp; ptr, const uint8_t* end, T&amp; value)
+{
+    if (ptr &gt; end - sizeof(value))
+        return false;
+
+    value = 0;
+    for (size_t i = 0; i &lt; sizeof(T); i++)
+        value += ((T)*ptr++) &lt;&lt; (i * 8);
+    return true;
+}
+#else
+template &lt;typename T&gt; static void writeLittleEndian(Vector&lt;char&gt;&amp; buffer, T value)
+{
+    buffer.append(reinterpret_cast&lt;uint8_t*&gt;(&amp;value), sizeof(value));
+}
+
+template &lt;typename T&gt; static bool readLittleEndian(const uint8_t*&amp; ptr, const uint8_t* end, T&amp; value)
+{
+    if (ptr &gt; end - sizeof(value))
+        return false;
+
+    value = *reinterpret_cast&lt;const T*&gt;(ptr);
+    ptr += sizeof(T);
+
+    return true;
+}
+#endif
+
+static void writeDouble(Vector&lt;char&gt;&amp; data, double d)
+{
+    writeLittleEndian(data, *reinterpret_cast&lt;uint64_t*&gt;(&amp;d));
+}
+
+static bool readDouble(const uint8_t*&amp; data, const uint8_t* end, double&amp; d)
+{
+    return readLittleEndian(data, end, *reinterpret_cast&lt;uint64_t*&gt;(&amp;d));
+}
+
+static void encodeKey(Vector&lt;char&gt;&amp; data, const IDBKeyData&amp; key)
+{
+    SIDBKeyType type = serializedTypeForKeyType(key.type());
+    data.append(static_cast&lt;char&gt;(type));
+
+    switch (type) {
+    case SIDBKeyType::Number:
+        writeDouble(data, key.number());
+        break;
+    case SIDBKeyType::Date:
+        writeDouble(data, key.date());
+        break;
+    case SIDBKeyType::String: {
+        auto string = key.string();
+        uint32_t length = string.length();
+        writeLittleEndian(data, length);
+
+        for (size_t i = 0; i &lt; length; ++i)
+            writeLittleEndian(data, string[i]);
+
+        break;
+    }
+    case SIDBKeyType::Binary: {
+        auto&amp; buffer = key.binary();
+        uint64_t size = buffer.size();
+        writeLittleEndian(data, size);
+
+        auto* bufferData = buffer.data();
+        ASSERT(bufferData || !size);
+        if (bufferData)
+            data.append(bufferData-&gt;data(), bufferData-&gt;size());
+
+        break;
+    }
+    case SIDBKeyType::Array: {
+        auto&amp; array = key.array();
+        uint64_t size = array.size();
+        writeLittleEndian(data, size);
+        for (auto&amp; key : array)
+            encodeKey(data, key);
+
+        break;
+    }
+    case SIDBKeyType::Min:
+    case SIDBKeyType::Max:
+        break;
+    }
+}
+
</ins><span class="cx"> RefPtr&lt;SharedBuffer&gt; serializeIDBKeyData(const IDBKeyData&amp; key)
</span><span class="cx"> {
</span><ins>+#if USE(CF)
+    Vector&lt;char&gt; data;
+    data.append(SIDBKeyVersion);
+
+    encodeKey(data, key);
+    return SharedBuffer::adoptVector(data);
+#else
</ins><span class="cx">     auto encoder = KeyedEncoder::encoder();
</span><span class="cx">     key.encode(*encoder);
</span><span class="cx">     return encoder-&gt;finishEncoding();
</span><ins>+#endif
+
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool decodeKey(const uint8_t*&amp; data, const uint8_t* end, IDBKeyData&amp; result)
+{
+    if (!data || data &gt;= end)
+        return false;
+
+    SIDBKeyType type = static_cast&lt;SIDBKeyType&gt;(data++[0]);
+    switch (type) {
+    case SIDBKeyType::Min:
+        result = IDBKeyData::minimum();
+        return true;
+    case SIDBKeyType::Max:
+        result = IDBKeyData::maximum();
+        return true;
+    case SIDBKeyType::Number: {
+        double d;
+        if (!readDouble(data, end, d))
+            return false;
+
+        result.setNumberValue(d);
+        return true;
+    }
+    case SIDBKeyType::Date: {
+        double d;
+        if (!readDouble(data, end, d))
+            return false;
+
+        result.setDateValue(d);
+        return true;
+    }
+    case SIDBKeyType::String: {
+        uint32_t length;
+        if (!readLittleEndian(data, end, length))
+            return false;
+
+        if (static_cast&lt;uint64_t&gt;(end - data) &lt; length * 2)
+            return false;
+
+        Vector&lt;UChar&gt; buffer;
+        buffer.reserveInitialCapacity(length);
+        for (size_t i = 0; i &lt; length; i++) {
+            uint16_t ch;
+            if (!readLittleEndian(data, end, ch))
+                return false;
+            buffer.uncheckedAppend(ch);
+        }
+
+        result.setStringValue(String::adopt(WTFMove(buffer)));
+
+        return true;
+    }
+    case SIDBKeyType::Binary: {
+        uint64_t size64;
+        if (!readLittleEndian(data, end, size64))
+            return false;
+
+        if (static_cast&lt;uint64_t&gt;(end - data) &lt; size64)
+            return false;
+
+        if (size64 &gt; std::numeric_limits&lt;size_t&gt;::max())
+            return false;
+
+        size_t size = static_cast&lt;size_t&gt;(size64);
+        Vector&lt;uint8_t&gt; dataVector;
+
+        dataVector.append(data, size);
+        data += size;
+
+        result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector));
+        return true;
+    }
+    case SIDBKeyType::Array: {
+        uint64_t size64;
+        if (!readLittleEndian(data, end, size64))
+            return false;
+
+        if (size64 &gt; std::numeric_limits&lt;size_t&gt;::max())
+            return false;
+
+        size_t size = static_cast&lt;size_t&gt;(size64);
+        Vector&lt;IDBKeyData&gt; array;
+        array.reserveInitialCapacity(size);
+
+        for (size_t i = 0; i &lt; size; ++i) {
+            IDBKeyData keyData;
+            if (!decodeKey(data, end, keyData))
+                return false;
+
+            ASSERT(keyData.isValid());
+            array.uncheckedAppend(WTFMove(keyData));
+        }
+
+        result.setArrayValue(array);
+
+        return true;
+    }
+    default:
+        LOG_ERROR(&quot;decodeKey encountered unexpected type: %i&quot;, (int)type);
+        return false;
+    }
+}
+
</ins><span class="cx"> bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData&amp; result)
</span><span class="cx"> {
</span><span class="cx">     if (!data || !size)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+#if USE(CF)
+    if (data[0] == LegacySerializedKeyVersion) {
+        auto decoder = KeyedDecoder::decoder(data, size);
+        return IDBKeyData::decode(*decoder, result);
+    }
+
+    // Verify this is a SerializedIDBKey version we understand.
+    const uint8_t* current = data;
+    const uint8_t* end = data + size;
+    if (current++[0] != SIDBKeyVersion)
+        return false;
+
+    if (decodeKey(current, end, result)) {
+        // Even if we successfully decoded a key, the deserialize is only successful
+        // if we actually consumed all input data.
+        return current == end;
+    }
+
+    return false;
+#else
</ins><span class="cx">     auto decoder = KeyedDecoder::decoder(data, size);
</span><span class="cx">     return IDBKeyData::decode(*decoder, result);
</span><ins>+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbserverSQLiteIDBBackingStorecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp (208770 => 208771)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp        2016-11-16 00:23:39 UTC (rev 208770)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp        2016-11-16 00:25:28 UTC (rev 208771)
</span><span class="lines">@@ -1183,6 +1183,8 @@
</span><span class="cx">         bool hasRecord;
</span><span class="cx">         IDBError error;
</span><span class="cx">         for (auto&amp; indexKey : indexKeys) {
</span><ins>+            if (!indexKey.isValid())
+                continue;
</ins><span class="cx">             error = uncheckedHasIndexRecord(info, indexKey, hasRecord);
</span><span class="cx">             if (!error.isNull())
</span><span class="cx">                 return error;
</span><span class="lines">@@ -1192,6 +1194,8 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     for (auto&amp; indexKey : indexKeys) {
</span><ins>+        if (!indexKey.isValid())
+            continue;
</ins><span class="cx">         auto error = uncheckedPutIndexRecord(info.objectStoreIdentifier(), info.identifier(), key, indexKey);
</span><span class="cx">         if (!error.isNull()) {
</span><span class="cx">             LOG_ERROR(&quot;Unable to put index record for newly created index&quot;);
</span></span></pre>
</div>
</div>

</body>
</html>