Title: [199730] trunk
Revision
199730
Author
beid...@apple.com
Date
2016-04-19 09:58:17 -0700 (Tue, 19 Apr 2016)

Log Message

Modern IDB: ObjectStore Blob Support.
https://bugs.webkit.org/show_bug.cgi?id=143193

Reviewed by Alex Christensen.

Source/WebCore:

Tests: imported/blink/storage/indexeddb/blob-basics-metadata.html
       imported/blink/storage/indexeddb/blob-delete-objectstore-db.html
       imported/blink/storage/indexeddb/blob-valid-after-deletion.html
       imported/blink/storage/indexeddb/blob-valid-before-commit.html
       imported/blink/storage/indexeddb/empty-blob-file.html
       storage/indexeddb/modern/blob-simple.html

Most of the work has been done already.

Besides a handful of tweaks to that work, all this really does is remove the clause
that prevents blob URLs from going into the database.

* Modules/indexeddb/IDBObjectStore.cpp:
(WebCore::IDBObjectStore::putOrAdd): Only disallow blobs if private browsing is enabled
  (Making that work is already covered by another bug)

* Modules/indexeddb/IDBTransaction.cpp:
(WebCore::IDBTransaction::putOrAddOnServer):

* Modules/indexeddb/IDBValue.cpp:
(WebCore::IDBValue::IDBValue):

* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteUnusedBlobFileRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteBackingStore):

* Modules/indexeddb/server/SQLiteIDBTransaction.cpp:
(WebCore::IDBServer::SQLiteIDBTransaction::deleteBlobFilesIfNecessary):

* platform/network/BlobRegistryImpl.cpp:
(WebCore::BlobRegistryImpl::writeBlobsToTemporaryFiles):

* platform/sql/SQLiteFileSystem.cpp:
(WebCore::SQLiteFileSystem::deleteDatabaseFile): Delete all database-related files
  now that we use WAL mode.

Source/WebKit2:

* NetworkProcess/NetworkConnectionToWebProcess.cpp:
(WebKit::NetworkConnectionToWebProcess::preregisterSandboxExtensionsForOptionallyFileBackedBlob): Don't ASSERT that
  this is the first we've heard of this path - The Connection now remembers all extensions.
(WebKit::NetworkConnectionToWebProcess::getBlobDataFileReferenceForPath): Don't take - Just get.

LayoutTests:

Note: The blob-basics-metadata.html test has some failure lines in it because we don't support the
File constructor.

Once support is added, that test will start failing with the correct results, at which point we
can update expectations.

* imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt: Added.
* imported/blink/storage/indexeddb/blob-basics-metadata.html: Added.
* imported/blink/storage/indexeddb/blob-delete-objectstore-db-expected.txt: Added.
* imported/blink/storage/indexeddb/blob-delete-objectstore-db.html: Added.
* imported/blink/storage/indexeddb/blob-valid-after-deletion-expected.txt: Added.
* imported/blink/storage/indexeddb/blob-valid-after-deletion.html: Added.
* imported/blink/storage/indexeddb/blob-valid-before-commit-expected.txt: Added.
* imported/blink/storage/indexeddb/blob-valid-before-commit.html: Added.
* imported/blink/storage/indexeddb/empty-blob-file-expected.txt: Added.
* imported/blink/storage/indexeddb/empty-blob-file.html: Added.
* imported/blink/storage/indexeddb/resources/empty.txt: Added.
* imported/blink/storage/indexeddb/resources/shared.js: Added.
* imported/blink/storage/indexeddb/resources/test-data.html: Added.
* imported/blink/storage/indexeddb/resources/test-data.txt: Added.

* platform/wk2/TestExpectations:

* storage/indexeddb/modern/blob-simple-expected.txt: Added.
* storage/indexeddb/modern/blob-simple.html: Added.
* storage/indexeddb/modern/resources/blob-simple.js: Added.

* storage/indexeddb/noblobs-expected.txt: Removed.
* storage/indexeddb/noblobs-private-expected.txt: Removed.
* storage/indexeddb/noblobs-private.html: Removed.
* storage/indexeddb/noblobs.html: Removed.
* storage/indexeddb/resources/noblobs.js: Removed.

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (199729 => 199730)


--- trunk/LayoutTests/ChangeLog	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/ChangeLog	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,3 +1,43 @@
+2016-04-19  Brady Eidson  <beid...@apple.com>
+
+        Modern IDB: ObjectStore Blob Support.
+        https://bugs.webkit.org/show_bug.cgi?id=143193
+
+        Reviewed by Alex Christensen.
+
+        Note: The blob-basics-metadata.html test has some failure lines in it because we don't support the 
+        File constructor.
+        
+        Once support is added, that test will start failing with the correct results, at which point we
+        can update expectations.
+
+        * imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt: Added.
+        * imported/blink/storage/indexeddb/blob-basics-metadata.html: Added.
+        * imported/blink/storage/indexeddb/blob-delete-objectstore-db-expected.txt: Added.
+        * imported/blink/storage/indexeddb/blob-delete-objectstore-db.html: Added.
+        * imported/blink/storage/indexeddb/blob-valid-after-deletion-expected.txt: Added.
+        * imported/blink/storage/indexeddb/blob-valid-after-deletion.html: Added.
+        * imported/blink/storage/indexeddb/blob-valid-before-commit-expected.txt: Added.
+        * imported/blink/storage/indexeddb/blob-valid-before-commit.html: Added.
+        * imported/blink/storage/indexeddb/empty-blob-file-expected.txt: Added.
+        * imported/blink/storage/indexeddb/empty-blob-file.html: Added.
+        * imported/blink/storage/indexeddb/resources/empty.txt: Added.
+        * imported/blink/storage/indexeddb/resources/shared.js: Added.
+        * imported/blink/storage/indexeddb/resources/test-data.html: Added.
+        * imported/blink/storage/indexeddb/resources/test-data.txt: Added.
+
+        * platform/wk2/TestExpectations:
+        
+        * storage/indexeddb/modern/blob-simple-expected.txt: Added.
+        * storage/indexeddb/modern/blob-simple.html: Added.
+        * storage/indexeddb/modern/resources/blob-simple.js: Added.
+        
+        * storage/indexeddb/noblobs-expected.txt: Removed.
+        * storage/indexeddb/noblobs-private-expected.txt: Removed.
+        * storage/indexeddb/noblobs-private.html: Removed.
+        * storage/indexeddb/noblobs.html: Removed.
+        * storage/indexeddb/resources/noblobs.js: Removed.
+
 2016-04-19  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r199726.

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,86 @@
+Confirm basic Blob/File/FileList functionality.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+dbname = "blob-basics-metadata.html"
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname)
+store = db.createObjectStore('storeName')
+store.put('value', 'key')
+
+testBlob():
+PASS FileReader != null is true
+test_content = 'This is a test. This is only a test.'
+blob = new Blob([test_content])
+
+validateResult(blob):
+PASS blob.size == test_content.length is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == test_content.length is true
+
+testFile():
+file = fileInput.files[0]
+
+validateResult(file):
+PASS file.name == fileInput.files[0].name is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(file, 'filekey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('filekey')
+PASS event.target.result.name == fileInput.files[0].name is true
+
+testNewFile():
+newFile = new File([test_content], 'filename', {type:'text/plain'})
+FAIL newFile = new File([test_content], 'filename', {type:'text/plain'}) threw exception TypeError: function is not a constructor (evaluating 'new File([test_content], 'filename', {type:'text/plain'})')
+
+validateResult(newFile):
+FAIL newFile.name == newFile.name should be true. Threw exception ReferenceError: Can't find variable: newFile
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(newFile, 'newFilekey')
+FAIL store.put(newFile, 'newFilekey') threw exception ReferenceError: Can't find variable: newFile
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('newFilekey')
+FAIL event.target.result.name == newFile.name should be true. Threw exception TypeError: undefined is not an object (evaluating 'event.target.result.name')
+
+testFileList():
+filelist = fileInput.files
+
+validateResult(filelist):
+PASS filelist[1].name == fileInput.files[1].name is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(filelist, 'filelistkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('filelistkey')
+PASS event.target.result[1].name == fileInput.files[1].name is true
+
+testCursor():
+transaction = db.transaction('storeName', 'readonly')
+store = transaction.objectStore('storeName')
+request = store.openCursor()
+PASS cursor.value.size == test_content.length is true
+cursor.continue();
+PASS cursor.value.name == fileInput.files[0].name is true
+cursor.continue();
+PASS cursor.value[1].name == fileInput.files[1].name is true
+cursor.continue();
+PASS cursor.value == 'value' is true
+cursor.continue();
+FAIL cursor.value.name == newFile.name should be true. Threw exception TypeError: null is not an object (evaluating 'cursor.value')
+cursor.continue();
+FAIL Unexpected error: TypeError: null is not an object (evaluating 'cursor.continue')
+FAIL successfullyParsed should be true. Was false.
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata.html (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<input type="file" id="fileInput" multiple></input>
+<script>
+
+description("Confirm basic Blob/File/FileList functionality.");
+
+fileInput = document.getElementById("fileInput");
+if (window.eventSender) {
+    var fileRect = fileInput.getClientRects()[0];
+    var targetX = fileRect.left + fileRect.width / 2;
+    var targetY = fileRect.top + fileRect.height / 2;
+    eventSender.beginDragWithFiles(['resources/test-data.html', 'resources/test-data.txt']);
+    eventSender.mouseMoveTo(targetX, targetY);
+    eventSender.mouseUp();
+}
+
+function prepareDatabase()
+{
+    db = event.target.result;
+    var trans = event.target.transaction;
+    evalAndLog("store = db.createObjectStore('storeName')");
+    evalAndLog("store.put('value', 'key')");
+    trans._onerror_ = unexpectedErrorCallback;
+    trans._onabort_ = unexpectedAbortCallback;
+}
+
+var blobValidation = ".size == test_content.length";
+function testBlob()
+{
+    preamble();
+
+    shouldBeTrue("FileReader != null");
+    evalAndLog("test_content = 'This is a test. This is only a test.'");
+    evalAndLog("blob = new Blob([test_content])");
+    validateResult("blob", blobValidation, testFile);
+}
+
+var fileValidation = ".name == fileInput.files[0].name";
+function testFile()
+{
+    preamble();
+    evalAndLog("file = fileInput.files[0]");
+    validateResult("file", fileValidation, testNewFile);
+}
+
+var newFileValidation = ".name == newFile.name";
+function testNewFile()
+{
+    debug("");
+    debug("testNewFile():");
+    evalAndLog("newFile = new File([test_content], 'filename', {type:'text/plain'})");
+    validateResult("newFile", newFileValidation, testFileList);
+}
+
+var fileListValidation = "[1].name == fileInput.files[1].name";
+function testFileList()
+{
+    preamble();
+    evalAndLog("filelist = fileInput.files");
+    validateResult("filelist",
+        fileListValidation, testCursor);
+}
+
+function testCursor()
+{
+    preamble();
+    evalAndLog("transaction = db.transaction('storeName', 'readonly')");
+    transaction._onerror_ = unexpectedErrorCallback;
+    transaction._onabort_ = unexpectedAbortCallback;
+    evalAndLog("store = transaction.objectStore('storeName')");
+    evalAndLog("request = store.openCursor()");
+
+    var count = 0;
+    request._onsuccess_ = continueToTest;
+
+    function continueToTest(event)
+    {
+        cursor = event.target.result;
+        switch (count++) {
+          case 0:
+            shouldBeTrue("cursor.value" + blobValidation);
+            break;
+          case 1:
+            shouldBeTrue("cursor.value" + fileValidation);
+            break;
+          case 2:
+            shouldBeTrue("cursor.value" + fileListValidation);
+            break;
+          case 3:
+            shouldBeTrue("cursor.value == 'value'");
+            break;
+          case 4:
+            shouldBeTrue("cursor.value" + newFileValidation);
+            break;
+          case 5:
+            shouldBeNull("cursor");
+            finishJSTest();
+            return;
+          default:
+            testFailed("count was unexpectedly " + count);
+        }
+        debug("cursor.continue();");
+        cursor.continue();
+//        evalAndLog("cursor.continue();");
+    }
+}
+
+function validateResult(variable, validation, onSuccess)
+{
+    var keyName = variable + "key";
+    debug("");
+    debug("validateResult(" + variable + "):");
+    shouldBeTrue(variable + validation);
+    evalAndLog("transaction = db.transaction('storeName', 'readwrite')");
+    evalAndLog("store = transaction.objectStore('storeName')");
+    evalAndLog("store.put(" + variable + ", '" + keyName + "')");
+    transaction._onerror_ = unexpectedErrorCallback;
+    transaction._onabort_ = unexpectedAbortCallback;
+    var _onGetSuccess_ = function (e) {
+      shouldBeTrue("event.target.result" + validation);
+      onSuccess();
+    }
+    transaction._oncomplete_ = function () {
+      doRead(keyName, onGetSuccess);
+    }
+}
+
+function doRead(keyName, onSuccess)
+{
+    evalAndLog("transaction = db.transaction('storeName', 'readwrite')");
+    evalAndLog("store = transaction.objectStore('storeName')");
+    evalAndLog("request = store.get('" + keyName + "')");
+    request._onsuccess_ = onSuccess;
+    transaction._onerror_ = unexpectedErrorCallback;
+    transaction._onabort_ = unexpectedAbortCallback;
+}
+
+if (window.eventSender) {
+    indexedDBTest(prepareDatabase, testBlob);
+} else {
+    alert("Select at least 2 files using the input control above to initiate the test");
+    document.getElementById("fileInput")._onchange_ = function() { indexedDBTest(prepareDatabase, testBlob); };
+}
+
+</script>

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-delete-objectstore-db-expected.txt (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-delete-objectstore-db-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-delete-objectstore-db-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,30 @@
+Test that deleting an object store and a database containing blobs doesn't crash.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+dbname = "blob-delete-objectstore-db.html"
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname, 1)
+store0 = db.createObjectStore('store0')
+store1 = db.createObjectStore('store1')
+store0.put(blobA, key)
+db.close()
+
+reopen():
+request = indexedDB.open(dbname, 2)
+
+deleteObjectStore():
+db.deleteObjectStore('store0')
+
+didDeleteObjectStore():
+trans = db.transaction('store1', 'readwrite')
+store1 = trans.objectStore('store1')
+store1.put(blobB, key)
+db.close()
+request = indexedDB.deleteDatabase(dbname)
+Database deleted.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-delete-objectstore-db.html (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-delete-objectstore-db.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-delete-objectstore-db.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,71 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script>
+
+description("Test that deleting an object store and a database containing blobs doesn't crash.");
+
+indexedDBTest(prepareDatabase, reopen, {version: 1});
+function prepareDatabase()
+{
+    db = event.target.result;
+    event.target.transaction._onabort_ = unexpectedAbortCallback;
+    evalAndLog("store0 = db.createObjectStore('store0')");
+    evalAndLog("store1 = db.createObjectStore('store1')");
+    blobAContent = "First blob content";
+    blobA = new Blob([blobAContent], {"type" : "text/plain"});
+    key = "blob key";
+    evalAndLog("store0.put(blobA, key)");
+}
+
+function reopen()
+{
+    evalAndLog("db.close()");
+    preamble();
+    evalAndLog("request = indexedDB.open(dbname, 2)");
+    request._onupgradeneeded_ = deleteObjectStore;
+    request._onsuccess_ = unexpectedSuccessCallback;
+    request._onerror_ = unexpectedErrorCallback;
+    request._onblocked_ = unexpectedBlockedCallback;
+}
+
+function deleteObjectStore(e)
+{
+    preamble();
+    db = e.target.result;
+    evalAndLog("db.deleteObjectStore('store0')");
+    request._onsuccess_ = didDeleteObjectStore;
+}
+
+function didDeleteObjectStore()
+{
+    preamble();
+    blobBContent = "Second blob content";
+    evalAndLog("trans = db.transaction('store1', 'readwrite')");
+    evalAndLog("store1 = trans.objectStore('store1')");
+    blobB = new Blob([blobBContent], {"type" : "text/plain"});
+    evalAndLog("store1.put(blobB, key)");
+    trans._oncomplete_ = deleteDatabase;
+    trans._onabort_ = unexpectedAbortCallback;
+}
+
+function deleteDatabase()
+{
+    evalAndLog("db.close()");
+    evalAndLog("request = indexedDB.deleteDatabase(dbname)");
+    request._onerror_ = unexpectedErrorCallback;
+    request._onsuccess_ = reportSuccess;
+}
+
+function reportSuccess()
+{
+    debug("Database deleted.");
+    finishJSTest();
+}
+</script>
+</body>
+</html>
+

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-after-deletion-expected.txt (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-after-deletion-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-after-deletion-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,25 @@
+Confirm that blobs stay alive after their records are deleted.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+dbname = "blob-valid-after-deletion.html"
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname)
+store = db.createObjectStore('store')
+store.put(value, key)
+
+doRead():
+trans = db.transaction('store')
+store = trans.objectStore('store')
+request = store.get(key)
+trans = db.transaction('store', 'readwrite')
+store = trans.objectStore('store')
+request = store.delete(key)
+PASS document.getElementById('frame0').contentDocument.body.innerText is blobAContent
+PASS document.getElementById('frame1').contentDocument.body.innerText is blobAContent
+PASS document.getElementById('frame2').contentDocument.body.innerText is blobBContent
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-after-deletion.html (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-after-deletion.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-after-deletion.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,86 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<iframe id="frame0"></iframe>
+<iframe id="frame1"></iframe>
+<iframe id="frame2"></iframe>
+<script>
+
+description("Confirm that blobs stay alive after their records are deleted.");
+
+indexedDBTest(prepareDatabase, doRead);
+function prepareDatabase()
+{
+    db = event.target.result;
+    event.target.transaction._onabort_ = unexpectedAbortCallback;
+    evalAndLog("store = db.createObjectStore('store')");
+    blobAContent = "Blob A content";
+    blobBContent = "Blob B content";
+    var blobA = new Blob([blobAContent], {"type" : "text/plain"});
+    var blobB = new Blob([blobBContent], {"type" : "text/plain"});
+    key = "key"
+    value = { a0: blobA, a1: blobA, b0: blobB };
+    evalAndLog("store.put(value, key)");
+    value = null;
+}
+
+function doRead()
+{
+    preamble();
+    evalAndLog("trans = db.transaction('store')");
+    evalAndLog("store = trans.objectStore('store')");
+    evalAndLog("request = store.get(key)");
+    request._onsuccess_ = didRead;
+}
+
+function didRead()
+{
+    record = request.result;
+    trans._oncomplete_ = doDelete;
+}
+
+function doDelete()
+{
+    evalAndLog("trans = db.transaction('store', 'readwrite')");
+    evalAndLog("store = trans.objectStore('store')");
+    evalAndLog("request = store.delete(key)");
+    trans._oncomplete_ = didDelete;
+}
+
+function didDelete()
+{
+    urlA0 = URL.createObjectURL(record.a0);
+    urlA1 = URL.createObjectURL(record.a1);
+    urlB = URL.createObjectURL(record.b0);
+    document.getElementById('frame0').src = ""
+    document.getElementById('frame0')._onload_ = verification;
+    document.getElementById('frame1').src = ""
+    document.getElementById('frame1')._onload_ = verification;
+    document.getElementById('frame2').src = ""
+    document.getElementById('frame2')._onload_ = verification;
+}
+
+var loadCount = 0;
+function verification()
+{
+    if (++loadCount < 3)
+        return;
+    URL.revokeObjectURL(urlA0);
+    URL.revokeObjectURL(urlA1);
+    URL.revokeObjectURL(urlB);
+    shouldBe("document.getElementById('frame0').contentDocument.body.innerText",
+        "blobAContent");
+    shouldBe("document.getElementById('frame1').contentDocument.body.innerText",
+        "blobAContent");
+    shouldBe("document.getElementById('frame2').contentDocument.body.innerText",
+        "blobBContent");
+    finishJSTest();
+}
+
+</script>
+</body>
+</html>
+

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-before-commit-expected.txt (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-before-commit-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-before-commit-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,18 @@
+Confirm that blobs can be read back before their records are committed.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+dbname = "blob-valid-before-commit.html"
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname)
+store = db.createObjectStore('store')
+store.put(value, key)
+request = store.get(key)
+PASS document.getElementById('frame0').contentDocument.body.innerText is blobAContent
+PASS document.getElementById('frame1').contentDocument.body.innerText is blobAContent
+PASS document.getElementById('frame2').contentDocument.body.innerText is blobBContent
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-before-commit.html (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-before-commit.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-valid-before-commit.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,65 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<iframe id="frame0"></iframe>
+<iframe id="frame1"></iframe>
+<iframe id="frame2"></iframe>
+<script>
+
+description("Confirm that blobs can be read back before their records are committed.");
+
+indexedDBTest(prepareDatabase);
+function prepareDatabase()
+{
+    db = event.target.result;
+    event.target.transaction._onabort_ = unexpectedAbortCallback;
+    evalAndLog("store = db.createObjectStore('store')");
+    blobAContent = "Blob A content";
+    blobBContent = "Blob B content";
+    var blobA = new Blob([blobAContent], {"type" : "text/plain"});
+    var blobB = new Blob([blobBContent], {"type" : "text/plain"});
+    key = "key"
+    value = { a0: blobA, a1: blobA, b0: blobB };
+    evalAndLog("store.put(value, key)");
+    evalAndLog("request = store.get(key)");
+    request._onsuccess_ = didRead;
+}
+
+function didRead()
+{
+    record = request.result;
+    urlA0 = URL.createObjectURL(record.a0);
+    urlA1 = URL.createObjectURL(record.a1);
+    urlB = URL.createObjectURL(record.b0);
+    document.getElementById('frame0').src = ""
+    document.getElementById('frame0')._onload_ = verification;
+    document.getElementById('frame1').src = ""
+    document.getElementById('frame1')._onload_ = verification;
+    document.getElementById('frame2').src = ""
+    document.getElementById('frame2')._onload_ = verification;
+}
+
+var loadCount = 0;
+function verification()
+{
+    if (++loadCount < 3)
+        return;
+    URL.revokeObjectURL(urlA0);
+    URL.revokeObjectURL(urlA1);
+    URL.revokeObjectURL(urlB);
+    shouldBe("document.getElementById('frame0').contentDocument.body.innerText",
+        "blobAContent");
+    shouldBe("document.getElementById('frame1').contentDocument.body.innerText",
+        "blobAContent");
+    shouldBe("document.getElementById('frame2').contentDocument.body.innerText",
+        "blobBContent");
+    finishJSTest();
+}
+
+</script>
+</body>
+</html>
+

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/empty-blob-file-expected.txt (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/empty-blob-file-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/empty-blob-file-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,120 @@
+Confirm that IndexedDB can store an empty Blob/File/FileList
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+dbname = "empty-blob-file.html"
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname)
+store = db.createObjectStore('storeName')
+store.put('value', 'key')
+
+testEmptyBlob():
+blob = new Blob([])
+
+validateResult(blob):
+PASS blob.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == 0 is true
+
+testEmptyDataBlob():
+blob = new Blob(['', '', ''])
+
+validateResult(blob):
+PASS blob.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == 0 is true
+
+testEmptyNestedBlob():
+blob = new Blob(['', new Blob([]), ''])
+
+validateResult(blob):
+PASS blob.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == 0 is true
+
+testEmptyNestedDataBlob():
+blob = new Blob(['', new Blob(['']), ''])
+
+validateResult(blob):
+PASS blob.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == 0 is true
+
+testEmptyFileInsideBlob():
+file = emptyFileInput.files[0]
+blob = new Blob(['', file, ''])
+
+validateResult(blob):
+PASS blob.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == 0 is true
+
+testEmptyFileInsideNestedBlob():
+file = emptyFileInput.files[0]
+blob = new Blob(['', new Blob([file]), ''])
+
+validateResult(blob):
+PASS blob.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(blob, 'blobkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('blobkey')
+PASS event.target.result.size == 0 is true
+
+testEmptyFile():
+file = emptyFileInput.files[0]
+
+validateResult(file):
+PASS file.size == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(file, 'filekey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('filekey')
+PASS event.target.result.size == 0 is true
+
+testEmptyFileList():
+fileList = emptyFileListInput.files
+
+validateResult(fileList):
+PASS fileList.length == 0 is true
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+store.put(fileList, 'fileListkey')
+transaction = db.transaction('storeName', 'readwrite')
+store = transaction.objectStore('storeName')
+request = store.get('fileListkey')
+PASS event.target.result.length == 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ 

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/empty-blob-file.html (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/empty-blob-file.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/empty-blob-file.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<input type="file" id="emptyFileInput"></input>
+<input type="file" id="emptyFileListInput" multiple></input>
+<script>
+
+description("Confirm that IndexedDB can store an empty Blob/File/FileList");
+
+var emptyFileInput = document.getElementById("emptyFileInput");
+var emptyFileListInput = document.getElementById("emptyFileListInput");
+if (window.eventSender) {
+    var fileRect = emptyFileInput.getClientRects()[0];
+    var targetX = fileRect.left + fileRect.width / 2;
+    var targetY = fileRect.top + fileRect.height / 2;
+    eventSender.beginDragWithFiles(['resources/empty.txt']);
+    eventSender.mouseMoveTo(targetX, targetY);
+    eventSender.mouseUp();
+}
+
+function prepareDatabase()
+{
+    db = event.target.result;
+    var trans = event.target.transaction;
+    evalAndLog("store = db.createObjectStore('storeName')");
+    evalAndLog("store.put('value', 'key')");
+    trans._onerror_ = unexpectedErrorCallback;
+    trans._onabort_ = unexpectedAbortCallback;
+}
+
+var blobValidation = ".size == 0";
+function testEmptyBlob()
+{
+    preamble();
+    evalAndLog("blob = new Blob([])");
+    validateResult("blob", blobValidation, testEmptyDataBlob);
+}
+function testEmptyDataBlob()
+{
+    preamble();
+    evalAndLog("blob = new Blob(['', '', ''])");
+    validateResult("blob", blobValidation, testEmptyNestedBlob);
+}
+function testEmptyNestedBlob()
+{
+    preamble();
+    evalAndLog("blob = new Blob(['', new Blob([]), ''])");
+    validateResult("blob", blobValidation, testEmptyNestedDataBlob);
+}
+function testEmptyNestedDataBlob()
+{
+    preamble();
+    evalAndLog("blob = new Blob(['', new Blob(['']), ''])");
+    validateResult("blob", blobValidation, testEmptyFileInsideBlob);
+}
+function testEmptyFileInsideBlob()
+{
+    preamble();
+    evalAndLog("file = emptyFileInput.files[0]");
+    evalAndLog("blob = new Blob(['', file, ''])");
+    validateResult("blob", blobValidation, testEmptyFileInsideNestedBlob);
+}
+function testEmptyFileInsideNestedBlob()
+{
+    preamble();
+    evalAndLog("file = emptyFileInput.files[0]");
+    evalAndLog("blob = new Blob(['', new Blob([file]), ''])");
+    validateResult("blob", blobValidation, testEmptyFile);
+}
+
+var fileValidation = ".size == 0";
+function testEmptyFile()
+{
+    preamble();
+    evalAndLog("file = emptyFileInput.files[0]");
+    validateResult("file", fileValidation, testEmptyFileList);
+}
+
+var fileListValidation = ".length == 0";
+function testEmptyFileList()
+{
+    preamble();
+    evalAndLog("fileList = emptyFileListInput.files");
+    validateResult("fileList",
+        fileListValidation, finishJSTest);
+}
+
+function validateResult(variable, validation, onSuccess)
+{
+    var keyName = variable + "key";
+    debug("");
+    debug("validateResult(" + variable + "):");
+    shouldBeTrue(variable + validation);
+    evalAndLog("transaction = db.transaction('storeName', 'readwrite')");
+    evalAndLog("store = transaction.objectStore('storeName')");
+    evalAndLog("store.put(" + variable + ", '" + keyName + "')");
+    transaction._onerror_ = unexpectedErrorCallback;
+    transaction._onabort_ = unexpectedAbortCallback;
+    var _onGetSuccess_ = function (e) {
+      shouldBeTrue("event.target.result" + validation);
+      onSuccess();
+    }
+    transaction._oncomplete_ = function () {
+      doRead(keyName, onGetSuccess);
+    }
+}
+
+function doRead(keyName, onSuccess)
+{
+    evalAndLog("transaction = db.transaction('storeName', 'readwrite')");
+    evalAndLog("store = transaction.objectStore('storeName')");
+    evalAndLog("request = store.get('" + keyName + "')");
+    request._onsuccess_ = onSuccess;
+    transaction._onerror_ = unexpectedErrorCallback;
+    transaction._onabort_ = unexpectedAbortCallback;
+}
+
+if (window.eventSender) {
+    indexedDBTest(prepareDatabase, testEmptyBlob);
+} else {
+    alert("Select an empty file using the left input control above to initiate the test");
+    document.getElementById("emptyFileInput")._onchange_ = function() { indexedDBTest(prepareDatabase, testEmptyBlob); };
+}
+</script>

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/resources/empty.txt ( => )


Added: trunk/LayoutTests/imported/blink/storage/indexeddb/resources/shared.js
===================================================================
--- trunk/LayoutTests/imported/blink/storage/indexeddb/resources/shared.js	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/resources/shared.js	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,210 @@
+var jsTestIsAsync = true;
+if (self.importScripts && !self.postMessage) {
+    // Shared worker.  Make postMessage send to the newest client, which in
+    // our tests is the only client.
+
+    // Store messages for sending until we have somewhere to send them.
+    self.postMessage = function(message)
+    {
+        if (typeof self.pendingMessages === "undefined")
+            self.pendingMessages = [];
+        self.pendingMessages.push(message);
+    };
+    self._onconnect_ = function(event)
+    {
+        self.postMessage = function(message)
+        {
+            event.ports[0].postMessage(message);
+        };
+        // Offload any stored messages now that someone has connected to us.
+        if (typeof self.pendingMessages === "undefined")
+            return;
+        while (self.pendingMessages.length)
+            event.ports[0].postMessage(self.pendingMessages.shift());
+    };
+}
+
+function unexpectedSuccessCallback()
+{
+    testFailed("Success function called unexpectedly.");
+    finishJSTest();
+}
+
+function unexpectedErrorCallback(event)
+{
+    testFailed("Error function called unexpectedly: (" + event.target.error.name + ") " + event.target.error.message);
+    finishJSTest();
+}
+
+function unexpectedAbortCallback(e)
+{
+    testFailed("Abort function called unexpectedly! Message: [" + e.target.error.message + "]");
+    finishJSTest();
+}
+
+function unexpectedCompleteCallback()
+{
+    testFailed("oncomplete function called unexpectedly!");
+    finishJSTest();
+}
+
+function unexpectedBlockedCallback(e)
+{
+    testFailed("onblocked called unexpectedly. oldVersion = " + e.oldVersion + ", newVersion = " + e.newVersion);
+    finishJSTest();
+}
+
+function unexpectedUpgradeNeededCallback()
+{
+    testFailed("onupgradeneeded called unexpectedly");
+    finishJSTest();
+}
+
+function unexpectedVersionChangeCallback(e)
+{
+    testFailed("onversionchange called unexpectedly. oldVersion = " + e.oldVersion + ". newVersion = " + e.newVersion);
+    finishJSTest();
+}
+
+function evalAndExpectException(cmd, exceptionCode, exceptionName, _quiet)
+{
+    if (!_quiet)
+        debug("Expecting exception from " + cmd);
+    try {
+        eval(cmd);
+        testFailed("No exception thrown! Should have been " + exceptionCode);
+    } catch (e) {
+        code = e.code;
+        if (!_quiet)
+            testPassed("Exception was thrown.");
+        shouldBe("code", exceptionCode, _quiet);
+        if (exceptionName) {
+            ename = e.name;
+            shouldBe("ename", exceptionName, _quiet);
+        }
+        if (!_quiet)
+            debug("Exception message: " + e.message);
+    }
+}
+
+function evalAndExpectExceptionClass(cmd, expected)
+{
+    debug("Expecting " + expected + " exception from " + cmd);
+    try {
+        eval(cmd);
+        testFailed("No exception thrown!" );
+    } catch (e) {
+        testPassed("Exception was thrown.");
+        if (eval("e instanceof " + expected))
+            testPassed(cmd + " threw " + e);
+        else
+            testFailed("Expected " + expected + " but saw " + e);
+    }
+}
+
+function evalAndLogCallback(cmd) {
+  function callback() {
+    evalAndLog(cmd);
+  }
+  return callback;
+}
+
+// If this function is deleted, a standalone layout test exercising its
+// functionality should be added.
+function deleteAllObjectStores(db)
+{
+    while (db.objectStoreNames.length)
+        db.deleteObjectStore(db.objectStoreNames.item(0));
+    debug("Deleted all object stores.");
+}
+
+function setDBNameFromPath(suffix) {
+    var name = self.location.pathname.substring(1 + self.location.pathname.lastIndexOf("/"));
+    if (suffix)
+        name += suffix;
+    evalAndLog('dbname = "' + name + '"');
+}
+
+function preamble(evt)
+{
+    if (evt)
+        event = evt;
+    debug("");
+    debug(preamble.caller.name + "():");
+}
+
+// For Workers
+if (!self.DOMException) {
+    self.DOMException = {
+        INDEX_SIZE_ERR: 1,
+        DOMSTRING_SIZE_ERR: 2,
+        HIERARCHY_REQUEST_ERR: 3,
+        WRONG_DOCUMENT_ERR: 4,
+        INVALID_CHARACTER_ERR: 5,
+        NO_DATA_ALLOWED_ERR: 6,
+        NO_MODIFICATION_ALLOWED_ERR: 7,
+        NOT_FOUND_ERR: 8,
+        NOT_SUPPORTED_ERR: 9,
+        INUSE_ATTRIBUTE_ERR: 10,
+        INVALID_STATE_ERR: 11,
+        SYNTAX_ERR: 12,
+        INVALID_MODIFICATION_ERR: 13,
+        NAMESPACE_ERR: 14,
+        INVALID_ACCESS_ERR: 15,
+        VALIDATION_ERR: 16,
+        TYPE_MISMATCH_ERR: 17,
+        SECURITY_ERR: 18,
+        NETWORK_ERR: 19,
+        ABORT_ERR: 20,
+        URL_MISMATCH_ERR: 21,
+        QUOTA_EXCEEDED_ERR: 22,
+        TIMEOUT_ERR: 23,
+        INVALID_NODE_TYPE_ERR: 24,
+        DATA_CLONE_ERR: 25
+    };
+}
+
+function indexedDBTest(upgradeCallback, optionalOpenCallback, optionalParameters) {
+    if (optionalParameters && 'suffix' in optionalParameters) {
+        setDBNameFromPath(optionalParameters['suffix']);
+    } else {
+        setDBNameFromPath();
+    }
+    var deleteRequest = evalAndLog("indexedDB.deleteDatabase(dbname)");
+    deleteRequest._onerror_ = unexpectedErrorCallback;
+    deleteRequest._onblocked_ = unexpectedBlockedCallback;
+    deleteRequest._onsuccess_ = function() {
+        self.openRequest = null;
+        if (optionalParameters && 'version' in optionalParameters)
+            openRequest = evalAndLog("indexedDB.open(dbname, " + optionalParameters['version'] + ")");
+        else
+            openRequest = evalAndLog("indexedDB.open(dbname)");
+        shouldBe("openRequest.readyState", "'pending'", true/*quiet*/);
+        openRequest._onerror_ = unexpectedErrorCallback;
+        openRequest._onupgradeneeded_ = upgradeCallback;
+        openRequest._onblocked_ = unexpectedBlockedCallback;
+        if (optionalOpenCallback)
+            openRequest._onsuccess_ = optionalOpenCallback;
+        delete self.openRequest;
+        if (optionalParameters && 'runAfterOpen' in optionalParameters)
+            (optionalParameters['runAfterOpen'])();
+    };
+}
+
+function waitForRequests(requests, callback) {
+    var count = requests.length;
+
+    if (!count) {
+        callback(requests);
+        return;
+    }
+
+    requests.forEach(function(req) {
+        req._onsuccess_ = function() {
+            --count;
+            if (!count)
+                callback(requests);
+        };
+        req._onerror_ = unexpectedErrorCallback;
+    });
+}

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/resources/test-data.html (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/resources/test-data.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/resources/test-data.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<title>This is some test data</title>
+<h1>Test data!</h1>
+<p>Get your test data here!

Added: trunk/LayoutTests/imported/blink/storage/indexeddb/resources/test-data.txt (0 => 199730)


--- trunk/LayoutTests/imported/blink/storage/indexeddb/resources/test-data.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/resources/test-data.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean laoreet dolor id urna eleifend aliquet. Nulla vel dolor ipsum. Aliquam ut turpis nisl, in vulputate sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed congue magna vitae dolor feugiat vehicula. Sed volutpat, tellus vel varius vestibulum, purus quam mollis sapien, in condimentum leo neque sed nulla. Nunc quis porta elit. Pellentesque erat lectus, ultricies a lobortis id, faucibus id quam.

Modified: trunk/LayoutTests/platform/wk2/TestExpectations (199729 => 199730)


--- trunk/LayoutTests/platform/wk2/TestExpectations	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/platform/wk2/TestExpectations	2016-04-19 16:58:17 UTC (rev 199730)
@@ -677,10 +677,10 @@
 http/tests/local/formdata/send-form-data-with-empty-file-filename.html
 http/tests/local/formdata/upload-events.html
 http/tests/security/clipboard/clipboard-file-access.html
+imported/blink/storage/indexeddb/blob-basics-metadata.html
+imported/blink/storage/indexeddb/empty-blob-file.html
 media/video-with-blob-url-allowed-by-csp-media-src-star.html
 media/video-src-blob.html
-storage/indexeddb/noblobs.html
-storage/indexeddb/noblobs-private.html
 storage/indexeddb/structured-clone.html
 storage/indexeddb/structured-clone-private.html
 

Added: trunk/LayoutTests/storage/indexeddb/modern/blob-simple-expected.txt (0 => 199730)


--- trunk/LayoutTests/storage/indexeddb/modern/blob-simple-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/modern/blob-simple-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,50 @@
+This tests basic operations putting blobs into an object store and then retrieving them.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
+
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname)
+Initial upgrade needed: Old version - 0 New version - 1
+Let's create a blob and store it in IndexedDB twice.
+Added blob to database once
+Added blob to database twice
+Let's retrieve the blob again and verify the contents is the same.
+Got blob from database
+Correct text: true
+Let's retrieve it again, create an object URL for the blob, load it via an XMLHttpRequest, and verify the contents are the same.
+Got blob from database
+Correct responseText: true
+Retrieve both blob entries from the database and verify contents.
+Correct text on first value: true
+Correct text on second value: true
+Got item from cursor
+Got item from cursor
+Finished cursor
+Got right number of items: true
+Correct text: true
+Retrieve blobs from database via index and verify contents.
+Got blob from database
+Correct text: true
+Got item from cursor
+Got item from cursor
+Finished cursor
+Got right number of items: true
+Correct text: true
+Correct text: true
+Slice the the retrieved blob and verify its contents.
+Correct text: true
+Send blob to a worker, read its contents there, and verify results.
+Correct text: true
+Correct text: true
+Store a blob back in the database, and keep holding on to the blob, verifying that it still can be read.
+Got blob from database
+Stored blob back into database
+Correct text: true
+Correct responseText: true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/storage/indexeddb/modern/blob-simple.html (0 => 199730)


--- trunk/LayoutTests/storage/indexeddb/modern/blob-simple.html	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/modern/blob-simple.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,10 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/storage/indexeddb/modern/resources/blob-simple.js (0 => 199730)


--- trunk/LayoutTests/storage/indexeddb/modern/resources/blob-simple.js	                        (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/modern/resources/blob-simple.js	2016-04-19 16:58:17 UTC (rev 199730)
@@ -0,0 +1,289 @@
+// Original implementation of test from Mozilla: ./dom/indexedDB/test/test_blob_simple.html
+description("This tests basic operations putting blobs into an object store and then retrieving them.");
+
+indexedDBTest(prepareDatabase);
+
+function done()
+{
+    finishJSTest();
+}
+
+function log(message)
+{
+    debug(message);
+}
+
+var testGenerator;
+
+function continueWithEvent(event)
+{
+    testGenerator.next(event);
+}
+
+function asyncContinue()
+{
+    setTimeout("testGenerator.next();", 0);
+}
+
+function idbRequest(request)
+{
+    request._onerror_ = continueWithEvent;
+    request._onsuccess_ = continueWithEvent;
+}
+
+var db;
+
+function prepareDatabase(event)
+{
+    debug("Initial upgrade needed: Old version - " + event.oldVersion + " New version - " + event.newVersion);
+
+    db = event.target.result;
+    db.createObjectStore("foo", { autoIncrement: true }).createIndex("foo", "index");
+    event.target._onsuccess_ = function() {
+        testGenerator = testSteps();
+        testGenerator.next();
+    };
+}
+
+function is(a, b, message) {
+    result = a == b ? "true" : "false";
+    debug(message + ": " + result);
+}
+
+function* testSteps()
+{
+    debug("Let's create a blob and store it in IndexedDB twice.");
+
+    const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
+    const INDEX_KEY = 5;
+    let blob = new Blob(BLOB_DATA, { type: "text/plain" });
+    let data = { blob: blob, index: INDEX_KEY };
+
+    objectStore = db.transaction("foo", "readwrite").objectStore("foo");
+    
+    idbRequest(objectStore.add(data));
+    event = yield;
+    
+    debug("Added blob to database once");
+
+    let key1 = event.target.result;
+
+    idbRequest(objectStore.add(data));
+    event = yield;
+
+    debug("Added blob to database twice");
+    
+    let key2 = event.target.result;
+
+    debug("Let's retrieve the blob again and verify the contents is the same.");
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    idbRequest(objectStore.get(key1));
+    event = yield;
+
+    debug("Got blob from database");
+    
+    let fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(event.target.result.blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    debug("Let's retrieve it again, create an object URL for the blob, load it via an XMLHttpRequest, and verify the contents are the same.");
+    
+    objectStore = db.transaction("foo").objectStore("foo");
+    idbRequest(objectStore.get(key1));
+    event = yield;
+    
+    debug("Got blob from database");
+    
+    let blobURL = URL.createObjectURL(event.target.result.blob);
+    
+    let xhr = new XMLHttpRequest();
+    xhr.open("GET", blobURL);
+    xhr._onload_ = continueWithEvent;
+    xhr.send();
+    yield;
+    
+    URL.revokeObjectURL(blobURL);    
+    is(xhr.responseText, BLOB_DATA.join(""), "Correct responseText");
+    
+    debug("Retrieve both blob entries from the database and verify contents.");
+
+    var objects = []
+    
+    objectStore = db.transaction("foo").objectStore("foo");
+    idbRequest(objectStore.get(key1));
+    event = yield;
+    
+    objects.push(event.target.result);
+    
+    objectStore = db.transaction("foo").objectStore("foo");
+    idbRequest(objectStore.get(key2));
+    event = yield;
+    
+    objects.push(event.target.result);
+        
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(objects[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text on first value");
+
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(objects[1].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text on second value");
+
+    let cursorResults = [];
+    
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.openCursor()._onsuccess_ = function(event) {
+      let cursor = event.target.result;
+      if (cursor) {
+        debug("Got item from cursor");
+        cursorResults.push(cursor.value);
+        cursor.continue();
+      }
+      else {
+        debug("Finished cursor");
+        asyncContinue();
+      }
+    };
+    yield;
+
+    is(cursorResults.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(cursorResults[0].blob);
+    event = yield;
+    
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    debug("Retrieve blobs from database via index and verify contents.");
+    
+    index = db.transaction("foo").objectStore("foo").index("foo");
+    idbRequest(index.get(INDEX_KEY)); 
+    event = yield;
+    
+    debug("Got blob from database");
+    
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(event.target.result.blob);
+    event = yield;
+    
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+        
+    cursorResults = [];
+    
+    index = db.transaction("foo").objectStore("foo").index("foo");
+    index.openCursor()._onsuccess_ = function(event) {
+      let cursor = event.target.result;
+      if (cursor) {
+        debug("Got item from cursor");
+        cursorResults.push(cursor.value);
+        cursor.continue();
+      }
+      else {
+        debug("Finished cursor");
+        asyncContinue();
+      }
+    };
+    yield;
+    
+    is(cursorResults.length, 2, "Got right number of items");
+    
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(cursorResults[0].blob);
+    event = yield;
+    
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+    
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(cursorResults[1].blob);
+    event = yield;
+    
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+    
+    debug("Slice the the retrieved blob and verify its contents.");
+    
+    let slice = cursorResults[1].blob.slice(0, BLOB_DATA[0].length);
+    
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(slice);
+    event = yield;
+    
+    is(event.target.result, BLOB_DATA[0], "Correct text");
+    
+    debug("Send blob to a worker, read its contents there, and verify results.");
+    
+    function workerScript() {
+        _onmessage_ = function(event) {
+            var reader = new FileReaderSync();
+            postMessage(reader.readAsText(event.data));
+    
+            var slice = event.data.slice(1, 2);
+            postMessage(reader.readAsText(slice));
+        }
+    }
+    
+    let url = "" Blob(["(", workerScript, ")()"]));
+    
+    let worker = new Worker(url);
+    worker.postMessage(slice);
+    worker._onmessage_ = continueWithEvent;
+    event = yield;
+    
+    is(event.data, BLOB_DATA[0], "Correct text");
+    event = yield;
+
+    is(event.data, BLOB_DATA[0][1], "Correct text");
+    
+    debug("Store a blob back in the database, and keep holding on to the " +
+         "blob, verifying that it still can be read.");
+    
+    objectStore = db.transaction("foo").objectStore("foo");
+    idbRequest(objectStore.get(key1)); 
+    event = yield;
+    
+    let blobFromDB = event.target.result.blob;
+    debug("Got blob from database");
+    
+    let txn = db.transaction("foo", "readwrite");
+    txn.objectStore("foo").put(event.target.result, key1);
+    txn._oncomplete_ = continueWithEvent;
+    event = yield;
+    
+    debug("Stored blob back into database");
+    
+    fileReader = new FileReader();
+    fileReader._onload_ = continueWithEvent;
+    fileReader.readAsText(blobFromDB);
+    event = yield;
+    
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+    
+    blobURL = URL.createObjectURL(blobFromDB);
+    
+    xhr = new XMLHttpRequest();
+    xhr.open("GET", blobURL);
+    xhr._onload_ = continueWithEvent;
+    xhr.send();
+    yield;
+    
+    URL.revokeObjectURL(blobURL);
+    
+    is(xhr.responseText, BLOB_DATA.join(""), "Correct responseText");
+    
+    finishJSTest();
+ }
+ 
\ No newline at end of file

Deleted: trunk/LayoutTests/storage/indexeddb/noblobs-expected.txt (199729 => 199730)


--- trunk/LayoutTests/storage/indexeddb/noblobs-expected.txt	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/storage/indexeddb/noblobs-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,80 +0,0 @@
-Confirm Blob/File/FileList limitations of WebKit's IndexedDB implementation.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
-
-indexedDB.deleteDatabase(dbname)
-indexedDB.open(dbname)
-store = db.createObjectStore('storeName')
-store.put('value', 'key')
-
-testBlob():
-PASS FileReader != null is true
-test_content = 'This is a test. This is only a test.'
-blob = new Blob([test_content])
-
-validateExceptions(blob):
-transaction = db.transaction('storeName', 'readwrite')
-store = transaction.objectStore('storeName')
-Expecting exception from store.put(blob, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-Expecting exception from store.add(blob, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-request = store.openCursor()
-cursor = request.result
-Expecting exception from cursor.update(blob)
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-
-testFile():
-file = fileInput.files[0]
-
-validateExceptions(file):
-transaction = db.transaction('storeName', 'readwrite')
-store = transaction.objectStore('storeName')
-Expecting exception from store.put(file, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-Expecting exception from store.add(file, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-request = store.openCursor()
-cursor = request.result
-Expecting exception from cursor.update(file)
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-
-testFileList():
-filelist = fileInput.files
-
-validateExceptions(filelist):
-transaction = db.transaction('storeName', 'readwrite')
-store = transaction.objectStore('storeName')
-Expecting exception from store.put(filelist, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-Expecting exception from store.add(filelist, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-request = store.openCursor()
-cursor = request.result
-Expecting exception from cursor.update(filelist)
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-

Deleted: trunk/LayoutTests/storage/indexeddb/noblobs-private-expected.txt (199729 => 199730)


--- trunk/LayoutTests/storage/indexeddb/noblobs-private-expected.txt	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/storage/indexeddb/noblobs-private-expected.txt	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,80 +0,0 @@
-Confirm Blob/File/FileList limitations of WebKit's IndexedDB implementation.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
-
-indexedDB.deleteDatabase(dbname)
-indexedDB.open(dbname)
-store = db.createObjectStore('storeName')
-store.put('value', 'key')
-
-testBlob():
-PASS FileReader != null is true
-test_content = 'This is a test. This is only a test.'
-blob = new Blob([test_content])
-
-validateExceptions(blob):
-transaction = db.transaction('storeName', 'readwrite')
-store = transaction.objectStore('storeName')
-Expecting exception from store.put(blob, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-Expecting exception from store.add(blob, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-request = store.openCursor()
-cursor = request.result
-Expecting exception from cursor.update(blob)
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-
-testFile():
-file = fileInput.files[0]
-
-validateExceptions(file):
-transaction = db.transaction('storeName', 'readwrite')
-store = transaction.objectStore('storeName')
-Expecting exception from store.put(file, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-Expecting exception from store.add(file, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-request = store.openCursor()
-cursor = request.result
-Expecting exception from cursor.update(file)
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-
-testFileList():
-filelist = fileInput.files
-
-validateExceptions(filelist):
-transaction = db.transaction('storeName', 'readwrite')
-store = transaction.objectStore('storeName')
-Expecting exception from store.put(filelist, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-Expecting exception from store.add(filelist, 'key')
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-request = store.openCursor()
-cursor = request.result
-Expecting exception from cursor.update(filelist)
-PASS Exception was thrown.
-PASS code is DOMException.DATA_CLONE_ERR
-Exception message: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-

Deleted: trunk/LayoutTests/storage/indexeddb/noblobs-private.html (199729 => 199730)


--- trunk/LayoutTests/storage/indexeddb/noblobs-private.html	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/storage/indexeddb/noblobs-private.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,10 +0,0 @@
-<html>
-<head>
-<script src=""
-<script src=""
-</head>
-<body>
-<input type="file" id="fileInput" multiple></input>
-<script src=""
-</body>
-</html>

Deleted: trunk/LayoutTests/storage/indexeddb/noblobs.html (199729 => 199730)


--- trunk/LayoutTests/storage/indexeddb/noblobs.html	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/storage/indexeddb/noblobs.html	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,10 +0,0 @@
-<html>
-<head>
-<script src=""
-<script src=""
-</head>
-<body>
-<input type="file" id="fileInput" multiple></input>
-<script src=""
-</body>
-</html>

Deleted: trunk/LayoutTests/storage/indexeddb/resources/noblobs.js (199729 => 199730)


--- trunk/LayoutTests/storage/indexeddb/resources/noblobs.js	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/LayoutTests/storage/indexeddb/resources/noblobs.js	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,78 +0,0 @@
-if (this.importScripts) {
-    importScripts('../../../resources/js-test.js');
-    importScripts('shared.js');
-}
-
-description("Confirm Blob/File/FileList limitations of WebKit's IndexedDB implementation.");
-// FIXME: This verifies that blob-type data is rejected for now, rather than silently failing.
-// Tracked for the Chromium port as: http://crbug.com/108012
-
-fileInput = document.getElementById("fileInput");
-if (window.eventSender) {
-    var fileRect = fileInput.getClientRects()[0];
-    var targetX = fileRect.left + fileRect.width / 2;
-    var targetY = fileRect.top + fileRect.height / 2;
-    eventSender.beginDragWithFiles(['resources/test-data.html', 'resources/test-data.txt']);
-    eventSender.mouseMoveTo(targetX, targetY);
-    eventSender.mouseUp();
-}
-
-function prepareDatabase()
-{
-    db = event.target.result;
-    var trans = event.target.transaction;
-    evalAndLog("store = db.createObjectStore('storeName')");
-    evalAndLog("store.put('value', 'key')");
-    trans._onerror_ = unexpectedErrorCallback;
-    trans._onabort_ = unexpectedAbortCallback;
-}
-
-function testBlob()
-{
-    debug("");
-    debug("testBlob():");
-
-    shouldBeTrue("FileReader != null");
-    evalAndLog("test_content = 'This is a test. This is only a test.'");
-    evalAndLog("blob = new Blob([test_content])");
-    validateExceptions("blob", testFile);
-}
-
-function testFile()
-{
-    debug("");
-    debug("testFile():");
-    evalAndLog("file = fileInput.files[0]");
-    validateExceptions("file", testFileList);
-}
-
-function testFileList()
-{
-    debug("");
-    debug("testFileList():");
-    evalAndLog("filelist = fileInput.files");
-    validateExceptions("filelist", finishJSTest);
-}
-
-function validateExceptions(variable, onComplete)
-{
-    debug("");
-    debug("validateExceptions(" + variable + "):");
-    evalAndLog("transaction = db.transaction('storeName', 'readwrite')");
-    evalAndLog("store = transaction.objectStore('storeName')");
-    evalAndExpectException("store.put(" + variable + ", 'key')", "DOMException.DATA_CLONE_ERR");
-    evalAndExpectException("store.add(" + variable + ", 'key')", "DOMException.DATA_CLONE_ERR");
-    evalAndLog("request = store.openCursor()");
-    request._onsuccess_ = function () {
-        evalAndLog("cursor = request.result");
-        evalAndExpectException("cursor.update(" + variable + ")", "DOMException.DATA_CLONE_ERR");
-    };
-    transaction._oncomplete_ = onComplete;
-}
-
-if (window.eventSender) {
-    indexedDBTest(prepareDatabase, testBlob);
-} else {
-    alert("Select file(s) using the input control above to initiate the test");
-    document.getElementById("fileInput")._onchange_ = function() { indexedDBTest(prepareDatabase, testBlob); };
-}

Modified: trunk/Source/WebCore/ChangeLog (199729 => 199730)


--- trunk/Source/WebCore/ChangeLog	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/ChangeLog	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,3 +1,46 @@
+2016-04-19  Brady Eidson  <beid...@apple.com>
+
+        Modern IDB: ObjectStore Blob Support.
+        https://bugs.webkit.org/show_bug.cgi?id=143193
+
+        Reviewed by Alex Christensen.
+
+        Tests: imported/blink/storage/indexeddb/blob-basics-metadata.html
+               imported/blink/storage/indexeddb/blob-delete-objectstore-db.html
+               imported/blink/storage/indexeddb/blob-valid-after-deletion.html
+               imported/blink/storage/indexeddb/blob-valid-before-commit.html
+               imported/blink/storage/indexeddb/empty-blob-file.html
+               storage/indexeddb/modern/blob-simple.html
+
+        Most of the work has been done already.
+        
+        Besides a handful of tweaks to that work, all this really does is remove the clause
+        that prevents blob URLs from going into the database.
+
+        * Modules/indexeddb/IDBObjectStore.cpp:
+        (WebCore::IDBObjectStore::putOrAdd): Only disallow blobs if private browsing is enabled
+          (Making that work is already covered by another bug)
+          
+        * Modules/indexeddb/IDBTransaction.cpp:
+        (WebCore::IDBTransaction::putOrAddOnServer):
+
+        * Modules/indexeddb/IDBValue.cpp:
+        (WebCore::IDBValue::IDBValue):
+
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteUnusedBlobFileRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteBackingStore):
+
+        * Modules/indexeddb/server/SQLiteIDBTransaction.cpp:
+        (WebCore::IDBServer::SQLiteIDBTransaction::deleteBlobFilesIfNecessary):
+
+        * platform/network/BlobRegistryImpl.cpp:
+        (WebCore::BlobRegistryImpl::writeBlobsToTemporaryFiles):
+
+        * platform/sql/SQLiteFileSystem.cpp:
+        (WebCore::SQLiteFileSystem::deleteDatabaseFile): Delete all database-related files 
+          now that we use WAL mode.
+
 2016-04-19  Sergio Villar Senin  <svil...@igalia.com>
 
         [css-grid] Use the margin box for non-auto minimum sizes

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp (199729 => 199730)


--- trunk/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -29,6 +29,7 @@
 #if ENABLE(INDEXED_DATABASE)
 
 #include "DOMStringList.h"
+#include "Document.h"
 #include "IDBBindingUtilities.h"
 #include "IDBCursor.h"
 #include "IDBDatabase.h"
@@ -41,6 +42,8 @@
 #include "IDBTransaction.h"
 #include "IndexedDB.h"
 #include "Logging.h"
+#include "Page.h"
+#include "ScriptExecutionContext.h"
 #include "ScriptState.h"
 #include "SerializedScriptValue.h"
 #include <wtf/Locker.h>
@@ -250,6 +253,13 @@
 {
     LOG(IndexedDB, "IDBObjectStore::putOrAdd");
 
+    auto context = scriptExecutionContextFromExecState(&state);
+    if (!context) {
+        ec.code = IDBDatabaseException::UnknownError;
+        ec.message = ASCIILiteral("Unable to store record in object store because it does not have a valid script execution context");
+        return nullptr;
+    }
+
     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
     // the exception for an object store being deleted.
     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
@@ -283,8 +293,14 @@
         return nullptr;
     }
 
-    if (serializedValue->hasBlobURLs()) {
-        // FIXME: Add Blob/File/FileList support
+    bool privateBrowsingEnabled = false;
+    if (context->isDocument()) {
+        if (auto* page = static_cast<Document*>(context)->page())
+            privateBrowsingEnabled = page->sessionID().isEphemeral();
+    }
+
+    if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) {
+        // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing.
         ec.code = IDBDatabaseException::DataCloneError;
         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.");
         return nullptr;
@@ -335,12 +351,6 @@
         return nullptr;
     }
 
-    auto context = scriptExecutionContextFromExecState(&state);
-    if (!context) {
-        ec.code = IDBDatabaseException::UnknownError;
-        return nullptr;
-    }
-
     return m_transaction->requestPutOrAdd(*context, *this, key.get(), *serializedValue, overwriteMode);
 }
 

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp (199729 => 199730)


--- trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -879,7 +879,17 @@
     RefPtr<IDBTransaction> protector(this);
     RefPtr<IDBClient::TransactionOperation> operationRef(&operation);
     value->writeBlobsToDiskForIndexedDB([protector, this, operationRef, key, value, overwriteMode](const IDBValue& idbValue) {
-        serverConnection().putOrAdd(*operationRef, key.get(), idbValue, overwriteMode);
+        if (idbValue.data().data()) {
+            serverConnection().putOrAdd(*operationRef, key.get(), idbValue, overwriteMode);
+            return;
+        }
+
+        // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
+        // In that case, we cannot successfully store this record, so we callback with an error.
+        auto result = IDBResultData::error(operationRef->identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
+        callOnMainThread([protector, this, operationRef, result]() {
+            operationRef->completed(result);
+        });
     });
 }
 

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBValue.cpp (199729 => 199730)


--- trunk/Source/WebCore/Modules/indexeddb/IDBValue.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBValue.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -52,6 +52,7 @@
     , m_blobURLs(blobURLs)
     , m_blobFilePaths(blobFilePaths)
 {
+    ASSERT(m_data.data());
 }
 
 IDBValue::IDBValue(const ThreadSafeDataBuffer& value, Vector<String>&& blobURLs, Vector<String>&& blobFilePaths)

Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp (199729 => 199730)


--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1286,8 +1286,10 @@
         }
 
         int result = sql.step();
-        while (result == SQLITE_ROW)
-            removedBlobFilenames.add(sql.getColumnText(1));
+        while (result == SQLITE_ROW) {
+            removedBlobFilenames.add(sql.getColumnText(0));
+            result = sql.step();
+        }
 
         if (result != SQLITE_DONE) {
             LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
@@ -2062,7 +2064,8 @@
     String databaseDirectory = fullDatabaseDirectory();
     for (auto& file : blobFiles) {
         String fullPath = pathByAppendingComponent(databaseDirectory, file);
-        deleteFile(fullPath);
+        if (!deleteFile(fullPath))
+            LOG_ERROR("Error deleting blob file %s", fullPath.utf8().data());
     }
 
     if (m_sqliteDB) {
@@ -2071,6 +2074,7 @@
     }
 
     SQLiteFileSystem::deleteDatabaseFile(dbFilename);
+    SQLiteFileSystem::deleteEmptyDatabaseDirectory(fullDatabaseDirectory());
     SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_absoluteDatabaseDirectory);
 }
 

Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp (199729 => 199730)


--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -102,11 +102,14 @@
 
 void SQLiteIDBTransaction::deleteBlobFilesIfNecessary()
 {
+    if (m_blobRemovedFilenames.isEmpty())
+        return;
+
     String databaseDirectory = m_backingStore.fullDatabaseDirectory();
     for (auto& entry : m_blobRemovedFilenames) {
         String fullPath = pathByAppendingComponent(databaseDirectory, entry);
-        m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry);
-        m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry);
+        m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(fullPath);
+        m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(fullPath);
     }
 
     m_blobRemovedFilenames.clear();

Modified: trunk/Source/WebCore/platform/network/BlobRegistryImpl.cpp (199729 => 199730)


--- trunk/Source/WebCore/platform/network/BlobRegistryImpl.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/platform/network/BlobRegistryImpl.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -267,6 +267,7 @@
         if (!blobData) {
             Vector<String> filePaths;
             completionHandler(filePaths);
+            return;
         }
 
         for (auto& item : blobData->items()) {
@@ -276,7 +277,9 @@
                 break;
             case BlobDataItem::Type::File:
                 blobsForWriting.last().filePathsOrDataBuffers.append({ item.file()->path().isolatedCopy(), { } });
-
+                break;
+            default:
+                ASSERT_NOT_REACHED();
             }
         }
     }
@@ -295,7 +298,7 @@
             PlatformFileHandle file;
             String tempFilePath = openTemporaryFile(ASCIILiteral("Blob"), file);
 
-            ScopeGuard fileCloser([file, completionHandler]() {
+            ScopeGuard fileCloser([file]() {
                 PlatformFileHandle handle = file;
                 closeFile(handle);
             });
@@ -315,7 +318,7 @@
                 } else {
                     ASSERT(!part.first.isEmpty());
                     if (!appendFileContentsToFileHandle(part.first, file)) {
-                        LOG_ERROR("Failed copying File contents to a Blob temporary file for storage in IndexedDB");
+                        LOG_ERROR("Failed copying File contents to a Blob temporary file for storage in IndexedDB (%s to %s)", part.first.utf8().data(), tempFilePath.utf8().data());
                         return;
                     }
                 }

Modified: trunk/Source/WebCore/platform/sql/SQLiteFileSystem.cpp (199729 => 199730)


--- trunk/Source/WebCore/platform/sql/SQLiteFileSystem.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebCore/platform/sql/SQLiteFileSystem.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -85,7 +85,18 @@
 
 bool SQLiteFileSystem::deleteDatabaseFile(const String& fileName)
 {
-    return deleteFile(fileName);
+    String walFileName = makeString(fileName, ASCIILiteral("-wal"));
+    String shmFileName = makeString(fileName, ASCIILiteral("-shm"));
+
+    if (!deleteFile(fileName))
+        return false;
+
+    // Try to delete both the wal and shm files, whether or not they are actually there.
+    deleteFile(walFileName);
+    deleteFile(shmFileName);
+
+    // If either the wal or shm files remain after the delete attempt, the overall delete operation failed.
+    return !fileExists(walFileName) && !fileExists(shmFileName);
 }
 
 #if PLATFORM(IOS)

Modified: trunk/Source/WebKit2/ChangeLog (199729 => 199730)


--- trunk/Source/WebKit2/ChangeLog	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebKit2/ChangeLog	2016-04-19 16:58:17 UTC (rev 199730)
@@ -1,3 +1,15 @@
+2016-04-19  Brady Eidson  <beid...@apple.com>
+
+        Modern IDB: ObjectStore Blob Support.
+        https://bugs.webkit.org/show_bug.cgi?id=143193
+
+        Reviewed by Alex Christensen.
+
+        * NetworkProcess/NetworkConnectionToWebProcess.cpp:
+        (WebKit::NetworkConnectionToWebProcess::preregisterSandboxExtensionsForOptionallyFileBackedBlob): Don't ASSERT that
+          this is the first we've heard of this path - The Connection now remembers all extensions.
+        (WebKit::NetworkConnectionToWebProcess::getBlobDataFileReferenceForPath): Don't take - Just get.
+
 2016-04-19  Antti Koivisto  <an...@apple.com>
 
         Try to fix iOS build.

Modified: trunk/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp (199729 => 199730)


--- trunk/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -86,7 +86,7 @@
 
 void NetworkBlobRegistry::registerBlobURLOptionallyFileBacked(NetworkConnectionToWebProcess* connection, const URL& url, const URL& srcURL, const String& fileBackedPath)
 {
-    auto fileReference = connection->takeBlobDataFileReferenceForPath(fileBackedPath);
+    auto fileReference = connection->getBlobDataFileReferenceForPath(fileBackedPath);
     ASSERT(fileReference);
 
     blobRegistry().registerBlobURLOptionallyFileBacked(url, srcURL, WTFMove(fileReference));

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp (199729 => 199730)


--- trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp	2016-04-19 16:58:17 UTC (rev 199730)
@@ -276,17 +276,14 @@
 {
     ASSERT(filePaths.size() == handles.size());
 
-    for (size_t i = 0; i < filePaths.size(); ++i) {
+    for (size_t i = 0; i < filePaths.size(); ++i)
         auto result = m_blobDataFileReferences.add(filePaths[i], BlobDataFileReferenceWithSandboxExtension::create(filePaths[i], SandboxExtension::create(handles[i])));
-        ASSERT_UNUSED(result, result.isNewEntry);
-    }
 }
 
-RefPtr<WebCore::BlobDataFileReference> NetworkConnectionToWebProcess::takeBlobDataFileReferenceForPath(const String& path)
+RefPtr<WebCore::BlobDataFileReference> NetworkConnectionToWebProcess::getBlobDataFileReferenceForPath(const String& path)
 {
-    auto fileReference = m_blobDataFileReferences.take(path);
-    ASSERT(fileReference);
-    return fileReference;
+    ASSERT(m_blobDataFileReferences.contains(path));
+    return m_blobDataFileReferences.get(path);
 }
 
 void NetworkConnectionToWebProcess::registerBlobURLOptionallyFileBacked(const URL& url, const URL& srcURL, const String& fileBackedPath)

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h (199729 => 199730)


--- trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h	2016-04-19 16:29:54 UTC (rev 199729)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h	2016-04-19 16:58:17 UTC (rev 199730)
@@ -55,7 +55,7 @@
 
     void didCleanupResourceLoader(NetworkResourceLoader&);
 
-    RefPtr<WebCore::BlobDataFileReference> takeBlobDataFileReferenceForPath(const String& path);
+    RefPtr<WebCore::BlobDataFileReference> getBlobDataFileReferenceForPath(const String& path);
 
 private:
     NetworkConnectionToWebProcess(IPC::Connection::Identifier);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to