Diff
Modified: trunk/LayoutTests/ChangeLog (200031 => 200032)
--- trunk/LayoutTests/ChangeLog 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/LayoutTests/ChangeLog 2016-04-25 17:31:29 UTC (rev 200032)
@@ -1,3 +1,21 @@
+2016-04-25 Brady Eidson <beid...@apple.com>
+
+ Implement latest File object spec (including its constructor).
+ https://bugs.webkit.org/show_bug.cgi?id=156511
+
+ Reviewed by Darin Adler.
+
+ * fast/files/file-constructor-expected.txt: Added.
+ * fast/files/file-constructor.html: Added.
+
+ * http/tests/local/fileapi/file-last-modified-after-delete-expected.txt:
+ * http/tests/local/fileapi/script-tests/file-last-modified-after-delete.js:
+
+ * http/tests/local/fileapi/file-last-modified-expected.txt:
+ * http/tests/local/fileapi/script-tests/file-last-modified.js:
+
+ * imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt:
+
2016-04-25 Daniel Bates <daba...@apple.com>
REGRESSION (r196012): Subresource may be blocked by Content Security Policy if it only matches 'self'
Added: trunk/LayoutTests/fast/files/file-constructor-expected.txt (0 => 200032)
--- trunk/LayoutTests/fast/files/file-constructor-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/files/file-constructor-expected.txt 2016-04-25 17:31:29 UTC (rev 200032)
@@ -0,0 +1,104 @@
+Test the File constructor.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS (new File([], 'world.html')) instanceof window.File is true
+PASS (new File(['hello'], 'world.html')) instanceof window.File is true
+PASS (new File(['hello'], 'world.html', {})) instanceof window.File is true
+PASS (new File(['hello'], 'world.html', {type:'text/html'})) instanceof window.File is true
+PASS (new File(['hello'], 'world.html', {type:'text/html', endings:'native'})) instanceof window.File is true
+PASS (new File(['hello'], 'world.html', {type:'text/html', endings:'transparent'})) instanceof window.File is true
+PASS (new File([], 'world.html')) instanceof window.File is true
+PASS (new File()) threw exception TypeError: First argument to File constructor must be a valid sequence, was undefined or null.
+PASS (new File([])) threw exception TypeError: Second argument to File constructor must be a valid string, was undefined.
+PASS (new File([], null)) instanceof window.File is true
+PASS (new File([], 1)) instanceof window.File is true
+PASS (new File([], '')) instanceof window.File is true
+PASS (new File([], document)) instanceof window.File is true
+PASS new File('hello', 'world.html') threw exception TypeError: Value is not a sequence.
+PASS new File(0, 'world.html') threw exception TypeError: Value is not a sequence.
+PASS new File(null, 'world.html') threw exception TypeError: First argument to File constructor must be a valid sequence, was undefined or null.
+PASS (new File([], 'world.html')) instanceof window.File is true
+PASS (new File(['stringPrimitive'], 'world.html')) instanceof window.File is true
+PASS (new File([String('stringObject')], 'world.html')) instanceof window.File is true
+PASS (new File([new Blob], 'world.html')) instanceof window.File is true
+PASS (new File([new Blob([new Blob])], 'world.html')) instanceof window.File is true
+PASS (new Blob([new File([], 'world.txt')])) instanceof window.Blob is true
+PASS (new Blob([new Blob([new File([new Blob], 'world.txt')])])) instanceof window.Blob is true
+PASS (new File([new File([], 'world.txt')], 'world.html')) instanceof window.File is true
+PASS (new File([new Blob([new File([new Blob], 'world.txt')])], 'world.html')) instanceof window.File is true
+PASS (new File([12], 'world.html')).size is 2
+PASS (new File([[]], 'world.html')).size is 0
+PASS (new File([{}], 'world.html')).size is 15
+PASS (new File([document], 'world.html')).size is 21
+PASS (new File([toStringingObj], 'world.html')).size is 8
+PASS new File([throwingObj], 'world.html') threw exception Error.
+PASS (new File([], null)).name is 'null'
+PASS (new File([], 12)).name is '12'
+PASS (new File([], '')).name is ''
+PASS (new File([], {})).name is '[object Object]'
+PASS (new File([], document)).name is '[object HTMLDocument]'
+PASS (new File([], toStringingObj)).name is 'A string'
+PASS (new File([], throwingObj)).name threw exception Error.
+PASS (new File([], 'world.html', {unknownKey:'value'})) instanceof window.File is true
+PASS new File([], 'world.html', {type:throwingObj}) threw exception Error.
+PASS (new File([], 'world.html', {type:'helloî'})) instanceof window.File is true
+PASS (new File([], 'world.html', {type:'helloî'})).type is ''
+PASS (new File([], 'world.html', {lastModified: 555, type:'goodbye'})).lastModified is 555
+PASS (new File([], 'world.html', {lastModified: 555, type:'goodbyeî'})).lastModified is not 555
+PASS (new File([], 'world.html', null)) instanceof window.File is true
+PASS (new File([], 'world.html', undefined)) instanceof window.File is true
+PASS (new File([], 'world.html', 123)) instanceof window.File threw exception TypeError: Third argument of the constructor is not of type Object.
+PASS (new File([], 'world.html', 123.4)) instanceof window.File threw exception TypeError: Third argument of the constructor is not of type Object.
+PASS (new File([], 'world.html', true)) instanceof window.File threw exception TypeError: Third argument of the constructor is not of type Object.
+PASS (new File([], 'world.html', 'abc')) instanceof window.File threw exception TypeError: Third argument of the constructor is not of type Object.
+PASS (new File([], 'world.html', [])) instanceof window.File is true
+PASS (new File([], 'world.html', /abc/)) instanceof window.File is true
+PASS (new File([], 'world.html', function () {})) instanceof window.File is true
+PASS (new File([], 'world.html')).name is 'world.html'
+PASS (new File([], 'w/orld/ht/m.l')).name is 'w:orld:ht:m.l'
+PASS (new File([], 'world.html', {type:'text/html'})).name is 'world.html'
+PASS (new File([], 'world.html', {type:'text/html'})).type is 'text/html'
+PASS (new File([], 'world.html', {type:'text/html'})).size is 0
+PASS (new File([], 'world.html', {type:'text/plain;charset=UTF-8'})).type is 'text/plain;charset=utf-8'
+PASS (new File([], 'world.html', {lastModified: 441532800000})).lastModified is 441532800000
+PASS (new File([], 'world.html')).lastModified is equivalent to Date.now().
+PASS (new File([], 'world.html', {})).lastModified is equivalent to Date.now().
+PASS (new File([], 'world.html', {type: 'text/plain'})).lastModified is equivalent to Date.now().
+PASS (new File([], 'world.html', {lastModified: new Date(441532800000)})).lastModified is 441532800000
+PASS window.File.length is 2
+PASS new File([new DataView(new ArrayBuffer(100))], 'world.html').size is 100
+PASS new File([new Uint8Array(100)], 'world.html').size is 100
+PASS new File([new Uint8ClampedArray(100)], 'world.html').size is 100
+PASS new File([new Uint16Array(100)], 'world.html').size is 200
+PASS new File([new Uint32Array(100)], 'world.html').size is 400
+PASS new File([new Int8Array(100)], 'world.html').size is 100
+PASS new File([new Int16Array(100)], 'world.html').size is 200
+PASS new File([new Int32Array(100)], 'world.html').size is 400
+PASS new File([new Float32Array(100)], 'world.html').size is 400
+PASS new File([new Float64Array(100)], 'world.html').size is 800
+PASS new File([new Float64Array(100), new Int32Array(100), new Uint8Array(100), new DataView(new ArrayBuffer(100))], 'world.html').size is 1400
+PASS new File([new Blob([new Int32Array(100)]), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))], 'world.html').size is 1000
+PASS new File([new Blob([new Int32Array(100)]), new File([new Uint16Array(100)], 'world.txt'), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))], 'world.html').size is 1200
+PASS new File([(new DataView(new ArrayBuffer(100))).buffer], 'world.html').size is 100
+PASS new File([(new Uint8Array(100)).buffer], 'world.html').size is 100
+PASS new File([(new Uint8ClampedArray(100)).buffer], 'world.html').size is 100
+PASS new File([(new Uint16Array(100)).buffer], 'world.html').size is 200
+PASS new File([(new Uint32Array(100)).buffer], 'world.html').size is 400
+PASS new File([(new Int8Array(100)).buffer], 'world.html').size is 100
+PASS new File([(new Int16Array(100)).buffer], 'world.html').size is 200
+PASS new File([(new Int32Array(100)).buffer], 'world.html').size is 400
+PASS new File([(new Float32Array(100)).buffer], 'world.html').size is 400
+PASS new File([(new Float64Array(100)).buffer], 'world.html').size is 800
+PASS new File([(new Float64Array(100)).buffer, (new Int32Array(100)).buffer, (new Uint8Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer], 'world.html').size is 1400
+PASS new File([new Blob([(new Int32Array(100)).buffer]), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer], 'world.html').size is 1000
+PASS new File([new Blob([(new Int32Array(100)).buffer]), new File([new Uint16Array(100).buffer], 'world.txt'), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer], 'world.html').size is 1200
+PASS new Blob([new Blob([new Int32Array(100)]), new File([new Uint16Array(100)], 'world.txt'), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))]).size is 1200
+PASS new Blob([new Blob([(new Int32Array(100)).buffer]), new File([new Uint16Array(100).buffer], 'world.txt'), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size is 1200
+PASS new File({length: 0}, 'world.txt').size is 0
+PASS new File({length: 1, 0: 'string'}, 'world.txt').size is 6
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/files/file-constructor.html (0 => 200032)
--- trunk/LayoutTests/fast/files/file-constructor.html (rev 0)
+++ trunk/LayoutTests/fast/files/file-constructor.html 2016-04-25 17:31:29 UTC (rev 200032)
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+
+<script src=""
+<script>
+description("Test the File constructor.");
+
+// Test the different ways you can construct a File.
+shouldBeTrue("(new File([], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File(['hello'], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File(['hello'], 'world.html', {})) instanceof window.File");
+shouldBeTrue("(new File(['hello'], 'world.html', {type:'text/html'})) instanceof window.File");
+shouldBeTrue("(new File(['hello'], 'world.html', {type:'text/html', endings:'native'})) instanceof window.File");
+shouldBeTrue("(new File(['hello'], 'world.html', {type:'text/html', endings:'transparent'})) instanceof window.File");
+
+// Test that File inherits from File.
+shouldBeTrue("(new File([], 'world.html')) instanceof window.File")
+
+// Verify that the file name argument is required.
+shouldThrow("(new File())", '"TypeError: First argument to File constructor must be a valid sequence, was undefined or null"');
+shouldThrow("(new File([]))", '"TypeError: Second argument to File constructor must be a valid string, was undefined"');
+
+// Test valid file names.
+shouldBeTrue("(new File([], null)) instanceof window.File");
+shouldBeTrue("(new File([], 1)) instanceof window.File");
+shouldBeTrue("(new File([], '')) instanceof window.File");
+shouldBeTrue("(new File([], document)) instanceof window.File");
+
+// Test invalid file parts.
+shouldThrow("new File('hello', 'world.html')", '"TypeError: Value is not a sequence"');
+shouldThrow("new File(0, 'world.html')", '"TypeError: Value is not a sequence"');
+shouldThrow("new File(null, 'world.html')", '"TypeError: First argument to File constructor must be a valid sequence, was undefined or null"');
+
+// Test valid file parts.
+shouldBeTrue("(new File([], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File(['stringPrimitive'], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File([String('stringObject')], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File([new Blob], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File([new Blob([new Blob])], 'world.html')) instanceof window.File");
+
+// Test File instances used as blob parts.
+shouldBeTrue("(new Blob([new File([], 'world.txt')])) instanceof window.Blob");
+shouldBeTrue("(new Blob([new Blob([new File([new Blob], 'world.txt')])])) instanceof window.Blob");
+shouldBeTrue("(new File([new File([], 'world.txt')], 'world.html')) instanceof window.File");
+shouldBeTrue("(new File([new Blob([new File([new Blob], 'world.txt')])], 'world.html')) instanceof window.File");
+
+// Test some conversions to string in the parts array.
+shouldBe("(new File([12], 'world.html')).size", "2");
+shouldBe("(new File([[]], 'world.html')).size", "0"); // [].toString() is the empty string
+shouldBe("(new File([{}], 'world.html')).size", "15");; // {}.toString() is the string "[object Object]"
+shouldBe("(new File([document], 'world.html')).size", "21"); // document.toString() is the string "[object HTMLDocument]"
+
+var toStringingObj = { toString: function() { return "A string"; } };
+shouldBe("(new File([toStringingObj], 'world.html')).size", "8");
+
+var throwingObj = { toString: function() { throw "Error"; } };
+shouldThrow("new File([throwingObj], 'world.html')", "'Error'");
+
+// Test some conversions to string in the file name.
+shouldBe("(new File([], null)).name", "'null'");
+shouldBe("(new File([], 12)).name", "'12'");
+shouldBe("(new File([], '')).name", "''");
+shouldBe("(new File([], {})).name", "'[object Object]'");
+shouldBe("(new File([], document)).name", "'[object HTMLDocument]'");
+shouldBe("(new File([], toStringingObj)).name", "'A string'");
+shouldThrow("(new File([], throwingObj)).name", "'Error'");
+
+// Test some invalid property bags.
+shouldBeTrue("(new File([], 'world.html', {unknownKey:'value'})) instanceof window.File");
+shouldThrow("new File([], 'world.html', {type:throwingObj})", "'Error'");
+
+// Type with non-ascii characters should become the empty string.
+shouldBeTrue("(new File([], 'world.html', {type:'hello\u00EE'})) instanceof window.File");
+shouldBe("(new File([], 'world.html', {type:'hello\u00EE'})).type", "''");
+
+// FilePropertyBag substeps: Type with non-ascii characters should prevent lastModified from being extracted.
+shouldBe("(new File([], 'world.html', {lastModified: 555, type:'goodbye'})).lastModified", "555");
+shouldNotBe("(new File([], 'world.html', {lastModified: 555, type:'goodbye\u00EE'})).lastModified", "555");
+
+// Test various non-object literals being used as property bags.
+shouldBeTrue("(new File([], 'world.html', null)) instanceof window.File");
+shouldBeTrue("(new File([], 'world.html', undefined)) instanceof window.File");
+shouldThrow("(new File([], 'world.html', 123)) instanceof window.File", "'TypeError: Third argument of the constructor is not of type Object'");
+shouldThrow("(new File([], 'world.html', 123.4)) instanceof window.File", "'TypeError: Third argument of the constructor is not of type Object'");
+shouldThrow("(new File([], 'world.html', true)) instanceof window.File", "'TypeError: Third argument of the constructor is not of type Object'");
+shouldThrow("(new File([], 'world.html', 'abc')) instanceof window.File", "'TypeError: Third argument of the constructor is not of type Object'");
+shouldBeTrue("(new File([], 'world.html', [])) instanceof window.File");
+shouldBeTrue("(new File([], 'world.html', /abc/)) instanceof window.File");
+shouldBeTrue("(new File([], 'world.html', function () {})) instanceof window.File");
+
+// Test that the name/type/size are correctly added to the File.
+shouldBe("(new File([], 'world.html')).name", "'world.html'");
+shouldBe("(new File([], 'w/orld/ht/m.l')).name", "'w:orld:ht:m.l'");
+shouldBe("(new File([], 'world.html', {type:'text/html'})).name", "'world.html'");
+shouldBe("(new File([], 'world.html', {type:'text/html'})).type", "'text/html'");
+shouldBe("(new File([], 'world.html', {type:'text/html'})).size", "0");
+shouldBe("(new File([], 'world.html', {type:'text/plain;charset=UTF-8'})).type", "'text/plain;charset=utf-8'");
+
+// Test that the lastModified is correctly added to the File.
+var aDate = new Date(441532800000);
+shouldBe("(new File([], 'world.html', {lastModified: 441532800000})).lastModified", "441532800000");
+// Unless specified, lastModified should default to now.
+shouldBeNow("(new File([], 'world.html')).lastModified", 20000);
+shouldBeNow("(new File([], 'world.html', {})).lastModified", 20000);
+shouldBeNow("(new File([], 'world.html', {type: 'text/plain'})).lastModified", 20000);
+// Test that Date instances are implicitly converted to numbers.
+shouldBe("(new File([], 'world.html', {lastModified: new Date(441532800000)})).lastModified", "441532800000");
+
+// Test the number of expected arguments in the File constructor.
+shouldBe("window.File.length", "2");
+
+// Test ArrayBufferView parameters.
+shouldBe("new File([new DataView(new ArrayBuffer(100))], 'world.html').size", "100");
+shouldBe("new File([new Uint8Array(100)], 'world.html').size", "100");
+shouldBe("new File([new Uint8ClampedArray(100)], 'world.html').size", "100");
+shouldBe("new File([new Uint16Array(100)], 'world.html').size", "200");
+shouldBe("new File([new Uint32Array(100)], 'world.html').size", "400");
+shouldBe("new File([new Int8Array(100)], 'world.html').size", "100");
+shouldBe("new File([new Int16Array(100)], 'world.html').size", "200");
+shouldBe("new File([new Int32Array(100)], 'world.html').size", "400");
+shouldBe("new File([new Float32Array(100)], 'world.html').size", "400");
+shouldBe("new File([new Float64Array(100)], 'world.html').size", "800");
+shouldBe("new File([new Float64Array(100), new Int32Array(100), new Uint8Array(100), new DataView(new ArrayBuffer(100))], 'world.html').size", "1400");
+shouldBe("new File([new Blob([new Int32Array(100)]), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))], 'world.html').size", "1000");
+shouldBe("new File([new Blob([new Int32Array(100)]), new File([new Uint16Array(100)], 'world.txt'), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))], 'world.html').size", "1200");
+
+// Test ArrayBuffer parameters.
+shouldBe("new File([(new DataView(new ArrayBuffer(100))).buffer], 'world.html').size", "100");
+shouldBe("new File([(new Uint8Array(100)).buffer], 'world.html').size", "100");
+shouldBe("new File([(new Uint8ClampedArray(100)).buffer], 'world.html').size", "100");
+shouldBe("new File([(new Uint16Array(100)).buffer], 'world.html').size", "200");
+shouldBe("new File([(new Uint32Array(100)).buffer], 'world.html').size", "400");
+shouldBe("new File([(new Int8Array(100)).buffer], 'world.html').size", "100");
+shouldBe("new File([(new Int16Array(100)).buffer], 'world.html').size", "200");
+shouldBe("new File([(new Int32Array(100)).buffer], 'world.html').size", "400");
+shouldBe("new File([(new Float32Array(100)).buffer], 'world.html').size", "400");
+shouldBe("new File([(new Float64Array(100)).buffer], 'world.html').size", "800");
+shouldBe("new File([(new Float64Array(100)).buffer, (new Int32Array(100)).buffer, (new Uint8Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer], 'world.html').size", "1400");
+shouldBe("new File([new Blob([(new Int32Array(100)).buffer]), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer], 'world.html').size", "1000");
+shouldBe("new File([new Blob([(new Int32Array(100)).buffer]), new File([new Uint16Array(100).buffer], 'world.txt'), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer], 'world.html').size", "1200");
+
+// Test building Blobs with ArrayBuffer / ArrayBufferView parts enclosed in files.
+shouldBe("new Blob([new Blob([new Int32Array(100)]), new File([new Uint16Array(100)], 'world.txt'), new Uint8Array(100), new Float32Array(100), new DataView(new ArrayBuffer(100))]).size", "1200");
+shouldBe("new Blob([new Blob([(new Int32Array(100)).buffer]), new File([new Uint16Array(100).buffer], 'world.txt'), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size", "1200");
+
+// Test passing blob parts in objects with indexed properties.
+// (This depends on the bindings code handling of sequence<T>)
+shouldBe("new File({length: 0}, 'world.txt').size", "0");
+shouldBe("new File({length: 1, 0: 'string'}, 'world.txt').size", "6");
+</script>
Modified: trunk/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt (200031 => 200032)
--- trunk/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/LayoutTests/http/tests/local/fileapi/file-last-modified-after-delete-expected.txt 2016-04-25 17:31:29 UTC (rev 200032)
@@ -4,9 +4,9 @@
PASS event.dataTransfer contains a File object on drop.
-PASS lastModifiedDate is not null
-PASS lastModifiedDate is >= testStartTime
-PASS (new Date()).getTime() is >= lastModifiedDate
+PASS lastModified is not null
+PASS lastModified is >= testStartTime.getTime()
+PASS (new Date()).getTime() is >= lastModified
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt (200031 => 200032)
--- trunk/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/LayoutTests/http/tests/local/fileapi/file-last-modified-expected.txt 2016-04-25 17:31:29 UTC (rev 200032)
@@ -4,7 +4,7 @@
PASS event.dataTransfer contains a File object on drop.
-PASS file.lastModifiedDate verified
+PASS file.lastModified verified
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/http/tests/local/fileapi/script-tests/file-last-modified-after-delete.js (200031 => 200032)
--- trunk/LayoutTests/http/tests/local/fileapi/script-tests/file-last-modified-after-delete.js 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/LayoutTests/http/tests/local/fileapi/script-tests/file-last-modified-after-delete.js 2016-04-25 17:31:29 UTC (rev 200032)
@@ -10,13 +10,13 @@
// Remove the temp file.
removeTempFile(tempFileName);
- // This synchronosly queries the file's lastModifiedDate (which should fail) until/unless we start capturing the file metadata at File construction time.
- lastModifiedDate = file.lastModifiedDate;
+ // This synchronosly queries the file's lastModified (which should fail) until/unless we start capturing the file metadata at File construction time.
+ lastModified = file.lastModified;
// The returned value should be equal to the current date/time since the file's modified date/time is not available.
- shouldNotBe('lastModifiedDate', 'null');
- shouldBeGreaterThanOrEqual('lastModifiedDate', 'testStartTime');
- shouldBeGreaterThanOrEqual('(new Date()).getTime()', 'lastModifiedDate');
+ shouldNotBe('lastModified', 'null');
+ shouldBeGreaterThanOrEqual('lastModified', 'testStartTime.getTime()');
+ shouldBeGreaterThanOrEqual('(new Date()).getTime()', 'lastModified');
}
function runTest()
Modified: trunk/LayoutTests/http/tests/local/fileapi/script-tests/file-last-modified.js (200031 => 200032)
--- trunk/LayoutTests/http/tests/local/fileapi/script-tests/file-last-modified.js 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/LayoutTests/http/tests/local/fileapi/script-tests/file-last-modified.js 2016-04-25 17:31:29 UTC (rev 200032)
@@ -6,11 +6,11 @@
xhr.open("GET", "http://127.0.0.1:8000/resources/file-last-modified.php?path=../local/fileapi/" + filePath, false);
xhr.send();
var expectedDate = new Date(parseInt(xhr.responseText) * 1000);
- var actualDate = file.lastModifiedDate;
- if (expectedDate.toString() == actualDate.toString())
- testPassed("file.lastModifiedDate verified");
+ var actualDate = file.lastModified;
+ if (expectedDate.getTime() == actualDate)
+ testPassed("file.lastModified verified");
else
- testFailed("file.lastModifiedDate incorrect");
+ testFailed("file.lastModified incorrect");
}
function runTest()
Modified: trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt (200031 => 200032)
--- trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/LayoutTests/imported/blink/storage/indexeddb/blob-basics-metadata-expected.txt 2016-04-25 17:31:29 UTC (rev 200032)
@@ -39,18 +39,16 @@
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
+PASS newFile.name == newFile.name is true
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')
+PASS event.target.result.name == newFile.name is true
testFileList():
filelist = fileInput.files
@@ -77,10 +75,10 @@
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')
+PASS cursor.value.name == newFile.name is true
cursor.continue();
-FAIL Unexpected error: TypeError: null is not an object (evaluating 'cursor.continue')
-FAIL successfullyParsed should be true. Was false.
+PASS cursor is null
+PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/Source/WebCore/CMakeLists.txt (200031 => 200032)
--- trunk/Source/WebCore/CMakeLists.txt 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/CMakeLists.txt 2016-04-25 17:31:29 UTC (rev 200032)
@@ -1143,6 +1143,7 @@
bindings/js/JSEventTargetCustom.cpp
bindings/js/JSExceptionBase.cpp
bindings/js/JSFetchResponseCustom.cpp
+ bindings/js/JSFileCustom.cpp
bindings/js/JSFileReaderCustom.cpp
bindings/js/JSGeolocationCustom.cpp
bindings/js/JSHTMLAllCollectionCustom.cpp
Modified: trunk/Source/WebCore/ChangeLog (200031 => 200032)
--- trunk/Source/WebCore/ChangeLog 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/ChangeLog 2016-04-25 17:31:29 UTC (rev 200032)
@@ -1,3 +1,29 @@
+2016-04-25 Brady Eidson <beid...@apple.com>
+
+ Implement latest File object spec (including its constructor).
+ https://bugs.webkit.org/show_bug.cgi?id=156511
+
+ Reviewed by Darin Adler.
+
+ Test: fast/files/file-constructor.html
+
+ * CMakeLists.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+
+ * bindings/js/JSDictionary.cpp:
+ (WebCore::JSDictionary::convertValue):
+ * bindings/js/JSDictionary.h:
+
+ * bindings/js/JSFileCustom.cpp: Added.
+ (WebCore::constructJSFile):
+
+ * fileapi/File.cpp:
+ (WebCore::File::File):
+ (WebCore::File::lastModified):
+ (WebCore::File::lastModifiedDate): Deleted.
+ * fileapi/File.h:
+ * fileapi/File.idl:
+
2016-04-25 Antti Koivisto <an...@apple.com>
REGRESSION(r156846): Crashes with guard malloc
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (200031 => 200032)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-04-25 17:31:29 UTC (rev 200032)
@@ -2058,6 +2058,7 @@
516D7D701BB5F0BD00AF7C77 /* IDBConnectionToServerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5185FCBD1BB5CB770012898F /* IDBConnectionToServerDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
516D7D711BB5F0BD00AF7C77 /* IDBConnectionToClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 516D7D6D1BB5F06500AF7C77 /* IDBConnectionToClient.cpp */; };
516D7D721BB5F0BD00AF7C77 /* IDBConnectionToClientDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 516D7D6E1BB5F06500AF7C77 /* IDBConnectionToClientDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 516E54FA1CCB2EA80040D954 /* JSFileCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 516E54F91CCB27FF0040D954 /* JSFileCustom.cpp */; };
516F7F6D1C31E39A00F111DC /* ServerOpenDBRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 516F7F6C1C31C79D00F111DC /* ServerOpenDBRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
516F7F6E1C31E39C00F111DC /* ServerOpenDBRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 516F7F6B1C31C79D00F111DC /* ServerOpenDBRequest.cpp */; };
517138EF1BED1D1A000D5F01 /* IndexKey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 517138ED1BED1D17000D5F01 /* IndexKey.cpp */; };
@@ -9644,6 +9645,7 @@
516C62241950E2B900337E75 /* JSGamepadEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGamepadEvent.h; sourceTree = "<group>"; };
516D7D6D1BB5F06500AF7C77 /* IDBConnectionToClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDBConnectionToClient.cpp; sourceTree = "<group>"; };
516D7D6E1BB5F06500AF7C77 /* IDBConnectionToClientDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IDBConnectionToClientDelegate.h; sourceTree = "<group>"; };
+ 516E54F91CCB27FF0040D954 /* JSFileCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFileCustom.cpp; sourceTree = "<group>"; };
516F7F6B1C31C79D00F111DC /* ServerOpenDBRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ServerOpenDBRequest.cpp; sourceTree = "<group>"; };
516F7F6C1C31C79D00F111DC /* ServerOpenDBRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerOpenDBRequest.h; sourceTree = "<group>"; };
517138ED1BED1D17000D5F01 /* IndexKey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IndexKey.cpp; sourceTree = "<group>"; };
@@ -22447,6 +22449,7 @@
BC2ED5540C6B9BD300920BFF /* JSElementCustom.cpp */,
ADEC78F718EE5308001315C2 /* JSElementCustom.h */,
BCEFAF4D0C317E6900FA81F6 /* JSEventCustom.cpp */,
+ 516E54F91CCB27FF0040D954 /* JSFileCustom.cpp */,
2E7582ED12764F260062628B /* JSFileReaderCustom.cpp */,
C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */,
1C24EEAA1C72AA0A0080F8FC /* JSFontFaceSetCustom.cpp */,
@@ -30243,6 +30246,7 @@
2E0888E6114884E200AF4265 /* JSDOMFormDataCustom.cpp in Sources */,
E1C36CBD0EB08062007410BC /* JSDOMGlobalObject.cpp in Sources */,
7C2BDD3D17C7F98C0038FF15 /* JSDOMGlobalObjectTask.cpp in Sources */,
+ 516E54FA1CCB2EA80040D954 /* JSFileCustom.cpp in Sources */,
65DF31F709D1CC60000BE325 /* JSDOMImplementation.cpp in Sources */,
A9D248060D757E7D00FDF959 /* JSDOMMimeType.cpp in Sources */,
A9D248080D757E7D00FDF959 /* JSDOMMimeTypeArray.cpp in Sources */,
Modified: trunk/Source/WebCore/bindings/js/JSDictionary.cpp (200031 => 200032)
--- trunk/Source/WebCore/bindings/js/JSDictionary.cpp 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/bindings/js/JSDictionary.cpp 2016-04-25 17:31:29 UTC (rev 200032)
@@ -98,6 +98,11 @@
result = value.toInt32(exec);
}
+void JSDictionary::convertValue(ExecState* exec, JSValue value, long int& result)
+{
+ result = value.toInt32(exec);
+}
+
void JSDictionary::convertValue(ExecState* exec, JSValue value, unsigned& result)
{
result = value.toUInt32(exec);
@@ -119,6 +124,12 @@
doubleToInteger(d, result);
}
+void JSDictionary::convertValue(ExecState* exec, JSValue value, long long& result)
+{
+ double d = value.toNumber(exec);
+ result = llrint(d);
+}
+
void JSDictionary::convertValue(ExecState* exec, JSValue value, double& result)
{
result = value.toNumber(exec);
Modified: trunk/Source/WebCore/bindings/js/JSDictionary.h (200031 => 200032)
--- trunk/Source/WebCore/bindings/js/JSDictionary.h 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/bindings/js/JSDictionary.h 2016-04-25 17:31:29 UTC (rev 200032)
@@ -117,10 +117,12 @@
static void convertValue(JSC::ExecState*, JSC::JSValue, bool& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, int& result);
+ static void convertValue(JSC::ExecState*, JSC::JSValue, long int& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, unsigned& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, unsigned short& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, unsigned long& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, unsigned long long& result);
+ static void convertValue(JSC::ExecState*, JSC::JSValue, long long& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, double& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, Dictionary& result);
static void convertValue(JSC::ExecState*, JSC::JSValue, String& result);
Added: trunk/Source/WebCore/bindings/js/JSFileCustom.cpp (0 => 200032)
--- trunk/Source/WebCore/bindings/js/JSFileCustom.cpp (rev 0)
+++ trunk/Source/WebCore/bindings/js/JSFileCustom.cpp 2016-04-25 17:31:29 UTC (rev 200032)
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSFile.h"
+
+#include "JSDOMBinding.h"
+#include "JSDictionary.h"
+#include "WebKitBlobBuilder.h"
+#include <runtime/Error.h>
+#include <runtime/JSArray.h>
+#include <runtime/JSArrayBuffer.h>
+#include <runtime/JSArrayBufferView.h>
+#include <wtf/Assertions.h>
+#include <wtf/CurrentTime.h>
+
+using namespace JSC;
+
+namespace WebCore {
+
+EncodedJSValue JSC_HOST_CALL constructJSFile(ExecState* exec)
+{
+ auto* constructor = jsCast<DOMConstructorObject*>(exec->callee());
+ ScriptExecutionContext* context = constructor->scriptExecutionContext();
+ if (!context)
+ return throwVMError(exec, createReferenceError(exec, "File constructor associated document is unavailable"));
+
+ JSValue arg = exec->argument(0);
+ if (arg.isUndefinedOrNull())
+ return throwVMError(exec, createTypeError(exec, "First argument to File constructor must be a valid sequence, was undefined or null"));
+
+ unsigned blobPartsLength = 0;
+ JSObject* blobParts = toJSSequence(exec, arg, blobPartsLength);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ ASSERT(blobParts);
+
+ arg = exec->argument(1);
+ if (arg.isUndefined())
+ return throwVMError(exec, createTypeError(exec, "Second argument to File constructor must be a valid string, was undefined"));
+
+ String filename = arg.toWTFString(exec).replace('/', ':');
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ String normalizedType;
+ Optional<int64_t> lastModified;
+
+ arg = exec->argument(2);
+ if (!arg.isUndefinedOrNull()) {
+ JSObject* filePropertyBagObject = arg.getObject();
+ if (!filePropertyBagObject)
+ return throwVMError(exec, createTypeError(exec, "Third argument of the constructor is not of type Object"));
+
+ // Create the dictionary wrapper from the initializer object.
+ JSDictionary dictionary(exec, filePropertyBagObject);
+
+ // Attempt to get the type property.
+ String type;
+ dictionary.get("type", type);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ normalizedType = Blob::normalizedContentType(type);
+
+ // Only try to parse the lastModified date if there was not an invalid type argument.
+ if (type.isEmpty() || !normalizedType.isEmpty()) {
+ dictionary.get("lastModified", lastModified);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
+ }
+
+ if (!lastModified)
+ lastModified = currentTimeMS();
+
+ BlobBuilder blobBuilder;
+
+ for (unsigned i = 0; i < blobPartsLength; ++i) {
+ JSValue item = blobParts->get(exec, i);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ if (ArrayBuffer* arrayBuffer = toArrayBuffer(item))
+ blobBuilder.append(arrayBuffer);
+ else if (RefPtr<ArrayBufferView> arrayBufferView = toArrayBufferView(item))
+ blobBuilder.append(WTFMove(arrayBufferView));
+ else if (Blob* blob = JSBlob::toWrapped(item))
+ blobBuilder.append(blob);
+ else {
+ String string = item.toWTFString(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ blobBuilder.append(string, ASCIILiteral("transparent"));
+ }
+ }
+
+ auto file = File::create(blobBuilder.finalize(), filename, normalizedType, lastModified.value());
+
+ return JSValue::encode(CREATE_DOM_WRAPPER(constructor->globalObject(), File, &file.get()));
+}
+
+} // namespace WebCore
+
Modified: trunk/Source/WebCore/fileapi/File.cpp (200031 => 200032)
--- trunk/Source/WebCore/fileapi/File.cpp 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/fileapi/File.cpp 2016-04-25 17:31:29 UTC (rev 200032)
@@ -64,8 +64,21 @@
{
}
-double File::lastModifiedDate() const
+File::File(Vector<BlobPart>&& blobParts, const String& filename, const String& contentType, int64_t lastModified)
+ : Blob(WTFMove(blobParts), contentType)
+ , m_name(filename)
+ , m_overrideLastModifiedDate(lastModified)
{
+}
+
+double File::lastModified() const
+{
+ if (m_overrideLastModifiedDate)
+ return m_overrideLastModifiedDate.value();
+
+ // FIXME: This does sync-i/o on the main thread and also recalculates every time the method is called.
+ // The i/o should be performed on a background thread,
+ // and the result should be cached along with an asynchronous monitor for changes to the file.
time_t modificationTime;
if (getFileModificationTime(m_path, modificationTime) && isValidFileTime(modificationTime))
return modificationTime * msPerSecond;
Modified: trunk/Source/WebCore/fileapi/File.h (200031 => 200032)
--- trunk/Source/WebCore/fileapi/File.h 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/fileapi/File.h 2016-04-25 17:31:29 UTC (rev 200032)
@@ -27,6 +27,7 @@
#define File_h
#include "Blob.h"
+#include <wtf/Optional.h>
#include <wtf/Ref.h>
#include <wtf/TypeCasts.h>
#include <wtf/text/WTFString.h>
@@ -42,6 +43,12 @@
return adoptRef(*new File(path));
}
+ // Create a File using the 'new File' constructor.
+ static Ref<File> create(Vector<BlobPart> blobParts, const String& filename, const String& contentType, int64_t lastModified)
+ {
+ return adoptRef(*new File(WTFMove(blobParts), filename, contentType, lastModified));
+ }
+
static Ref<File> deserialize(const String& path, const URL& srcURL, const String& type, const String& name)
{
return adoptRef(*new File(deserializationContructor, path, srcURL, type, name));
@@ -59,10 +66,8 @@
const String& path() const { return m_path; }
const String& name() const { return m_name; }
+ double lastModified() const;
- // This returns the current date and time if the file's last modification date is not known (per spec: http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate).
- double lastModifiedDate() const;
-
static String contentTypeForFile(const String& path);
#if ENABLE(FILE_REPLACEMENT)
@@ -72,6 +77,7 @@
private:
WEBCORE_EXPORT explicit File(const String& path);
File(const String& path, const String& nameOverride);
+ File(Vector<BlobPart>&& blobParts, const String& filename, const String& contentType, int64_t lastModified);
File(DeserializationContructor, const String& path, const URL& srcURL, const String& type, const String& name);
@@ -82,6 +88,9 @@
String m_path;
String m_name;
+
+ Optional<String> m_overrideFilename;
+ Optional<int64_t> m_overrideLastModifiedDate;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/fileapi/File.idl (200031 => 200032)
--- trunk/Source/WebCore/fileapi/File.idl 2016-04-25 17:03:04 UTC (rev 200031)
+++ trunk/Source/WebCore/fileapi/File.idl 2016-04-25 17:31:29 UTC (rev 200032)
@@ -24,13 +24,13 @@
*/
[
+ Exposed=(Window),
JSGenerateToNativeObject,
JSGenerateToJSObject,
ExportMacro=WEBCORE_EXPORT,
+ CustomConstructor(sequence<any> fileBits, DOMString fileName, optional FilePropertyBag options),
] interface File : Blob {
readonly attribute DOMString name;
-#if !defined(LANGUAGE_GOBJECT) || !LANGUAGE_GOBJECT
- readonly attribute Date lastModifiedDate;
-#endif
+ readonly attribute long long lastModified;
};