From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../FileAPI/Blob-methods-from-detached-frame.html | 59 +++ .../BlobURL/cross-partition.tentative.https.html | 276 ++++++++++++ .../tests/FileAPI/BlobURL/support/file_test2.txt | 0 .../tests/FileAPI/BlobURL/test2-manual.html | 62 +++ .../progress_event_bubbles_cancelable.html | 33 ++ .../FileAPI/FileReader/support/file_test1.txt | 0 .../FileAPI/FileReader/test_errors-manual.html | 72 ++++ .../FileReader/test_notreadableerrors-manual.html | 42 ++ .../FileReader/test_securityerrors-manual.html | 40 ++ .../tests/FileAPI/FileReader/workers.html | 27 ++ .../tests/FileAPI/FileReaderSync.worker.js | 56 +++ testing/web-platform/tests/FileAPI/META.yml | 6 + .../tests/FileAPI/blob/Blob-array-buffer.any.js | 45 ++ .../FileAPI/blob/Blob-constructor-dom.window.js | 53 +++ .../FileAPI/blob/Blob-constructor-endings.html | 104 +++++ .../tests/FileAPI/blob/Blob-constructor.any.js | 468 +++++++++++++++++++++ .../tests/FileAPI/blob/Blob-in-worker.worker.js | 9 + .../tests/FileAPI/blob/Blob-slice-overflow.any.js | 32 ++ .../tests/FileAPI/blob/Blob-slice.any.js | 231 ++++++++++ .../tests/FileAPI/blob/Blob-stream-byob-crash.html | 11 + .../FileAPI/blob/Blob-stream-sync-xhr-crash.html | 13 + .../tests/FileAPI/blob/Blob-stream.any.js | 94 +++++ .../tests/FileAPI/blob/Blob-text.any.js | 64 +++ .../FileAPI/file/File-constructor-endings.html | 104 +++++ .../tests/FileAPI/file/File-constructor.any.js | 155 +++++++ .../file/Worker-read-file-constructor.worker.js | 15 + .../FileAPI/file/resources/echo-content-escaped.py | 26 ++ .../FileAPI/file/send-file-form-controls.html | 113 +++++ .../FileAPI/file/send-file-form-iso-2022-jp.html | 65 +++ .../FileAPI/file/send-file-form-punctuation.html | 226 ++++++++++ .../tests/FileAPI/file/send-file-form-utf-8.html | 62 +++ .../FileAPI/file/send-file-form-windows-1252.html | 62 +++ .../file/send-file-form-x-user-defined.html | 63 +++ .../tests/FileAPI/file/send-file-form.html | 25 ++ .../file/send-file-formdata-controls.any.js | 69 +++ .../file/send-file-formdata-punctuation.any.js | 144 +++++++ .../FileAPI/file/send-file-formdata-utf-8.any.js | 33 ++ .../tests/FileAPI/file/send-file-formdata.any.js | 8 + .../web-platform/tests/FileAPI/fileReader.any.js | 59 +++ .../tests/FileAPI/filelist-section/filelist.html | 57 +++ .../filelist_multiple_selected_files-manual.html | 64 +++ .../filelist_selected_file-manual.html | 64 +++ .../FileAPI/filelist-section/support/upload.txt | 1 + .../FileAPI/filelist-section/support/upload.zip | Bin 0 -> 220 bytes .../tests/FileAPI/historical.https.html | 65 +++ .../tests/FileAPI/idlharness-manual.html | 45 ++ .../web-platform/tests/FileAPI/idlharness.any.js | 19 + testing/web-platform/tests/FileAPI/idlharness.html | 37 ++ .../tests/FileAPI/idlharness.worker.js | 17 + .../tests/FileAPI/progress-manual.html | 49 +++ .../Determining-Encoding.any.js | 81 ++++ .../FileReader-event-handler-attributes.any.js | 17 + .../FileReader-multiple-reads.any.js | 81 ++++ .../reading-data-section/filereader_abort.any.js | 38 ++ .../reading-data-section/filereader_error.any.js | 19 + .../reading-data-section/filereader_events.any.js | 19 + .../filereader_file-manual.html | 69 +++ .../filereader_file_img-manual.html | 47 +++ .../filereader_readAsArrayBuffer.any.js | 23 + .../filereader_readAsBinaryString.any.js | 23 + .../filereader_readAsDataURL.any.js | 54 +++ .../filereader_readAsText.any.js | 36 ++ .../filereader_readystate.any.js | 19 + .../reading-data-section/filereader_result.any.js | 82 ++++ .../reading-data-section/support/blue-100x100.png | Bin 0 -> 227 bytes testing/web-platform/tests/FileAPI/support/Blob.js | 70 +++ .../support/document-domain-setter.sub.html | 7 + .../tests/FileAPI/support/empty-document.html | 3 + .../FileAPI/support/historical-serviceworker.js | 5 + .../tests/FileAPI/support/incumbent.sub.html | 22 + .../tests/FileAPI/support/send-file-form-helper.js | 282 +++++++++++++ .../FileAPI/support/send-file-formdata-helper.js | 99 +++++ .../web-platform/tests/FileAPI/support/upload.txt | 1 + .../tests/FileAPI/support/url-origin.html | 6 + testing/web-platform/tests/FileAPI/unicode.html | 46 ++ .../tests/FileAPI/url/cross-global-revoke.sub.html | 62 +++ .../url/multi-global-origin-serialization.sub.html | 26 ++ .../tests/FileAPI/url/resources/create-helper.html | 7 + .../tests/FileAPI/url/resources/create-helper.js | 4 + .../tests/FileAPI/url/resources/fetch-tests.js | 71 ++++ .../tests/FileAPI/url/resources/revoke-helper.html | 7 + .../tests/FileAPI/url/resources/revoke-helper.js | 9 + .../tests/FileAPI/url/sandboxed-iframe.html | 32 ++ .../tests/FileAPI/url/unicode-origin.sub.html | 23 + .../tests/FileAPI/url/url-charset.window.js | 34 ++ .../tests/FileAPI/url/url-format.any.js | 70 +++ .../tests/FileAPI/url/url-in-tags-revoke.window.js | 115 +++++ .../tests/FileAPI/url/url-in-tags.window.js | 48 +++ .../tests/FileAPI/url/url-lifetime.html | 56 +++ .../tests/FileAPI/url/url-reload.window.js | 36 ++ .../tests/FileAPI/url/url-with-fetch.any.js | 72 ++++ .../tests/FileAPI/url/url-with-xhr.any.js | 68 +++ .../url/url_createobjecturl_file-manual.html | 45 ++ .../url/url_createobjecturl_file_img-manual.html | 28 ++ .../FileAPI/url/url_xmlhttprequest_img-ref.html | 12 + .../tests/FileAPI/url/url_xmlhttprequest_img.html | 27 ++ 96 files changed, 5445 insertions(+) create mode 100644 testing/web-platform/tests/FileAPI/Blob-methods-from-detached-frame.html create mode 100644 testing/web-platform/tests/FileAPI/BlobURL/cross-partition.tentative.https.html create mode 100644 testing/web-platform/tests/FileAPI/BlobURL/support/file_test2.txt create mode 100644 testing/web-platform/tests/FileAPI/BlobURL/test2-manual.html create mode 100644 testing/web-platform/tests/FileAPI/FileReader/progress_event_bubbles_cancelable.html create mode 100644 testing/web-platform/tests/FileAPI/FileReader/support/file_test1.txt create mode 100644 testing/web-platform/tests/FileAPI/FileReader/test_errors-manual.html create mode 100644 testing/web-platform/tests/FileAPI/FileReader/test_notreadableerrors-manual.html create mode 100644 testing/web-platform/tests/FileAPI/FileReader/test_securityerrors-manual.html create mode 100644 testing/web-platform/tests/FileAPI/FileReader/workers.html create mode 100644 testing/web-platform/tests/FileAPI/FileReaderSync.worker.js create mode 100644 testing/web-platform/tests/FileAPI/META.yml create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-array-buffer.any.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-constructor-dom.window.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-constructor-endings.html create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-constructor.any.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-in-worker.worker.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-slice-overflow.any.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-slice.any.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-stream-byob-crash.html create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-stream-sync-xhr-crash.html create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-stream.any.js create mode 100644 testing/web-platform/tests/FileAPI/blob/Blob-text.any.js create mode 100644 testing/web-platform/tests/FileAPI/file/File-constructor-endings.html create mode 100644 testing/web-platform/tests/FileAPI/file/File-constructor.any.js create mode 100644 testing/web-platform/tests/FileAPI/file/Worker-read-file-constructor.worker.js create mode 100644 testing/web-platform/tests/FileAPI/file/resources/echo-content-escaped.py create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form-controls.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form-iso-2022-jp.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form-punctuation.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form-utf-8.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form-windows-1252.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form-x-user-defined.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-form.html create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-formdata-controls.any.js create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-formdata-punctuation.any.js create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-formdata-utf-8.any.js create mode 100644 testing/web-platform/tests/FileAPI/file/send-file-formdata.any.js create mode 100644 testing/web-platform/tests/FileAPI/fileReader.any.js create mode 100644 testing/web-platform/tests/FileAPI/filelist-section/filelist.html create mode 100644 testing/web-platform/tests/FileAPI/filelist-section/filelist_multiple_selected_files-manual.html create mode 100644 testing/web-platform/tests/FileAPI/filelist-section/filelist_selected_file-manual.html create mode 100644 testing/web-platform/tests/FileAPI/filelist-section/support/upload.txt create mode 100644 testing/web-platform/tests/FileAPI/filelist-section/support/upload.zip create mode 100644 testing/web-platform/tests/FileAPI/historical.https.html create mode 100644 testing/web-platform/tests/FileAPI/idlharness-manual.html create mode 100644 testing/web-platform/tests/FileAPI/idlharness.any.js create mode 100644 testing/web-platform/tests/FileAPI/idlharness.html create mode 100644 testing/web-platform/tests/FileAPI/idlharness.worker.js create mode 100644 testing/web-platform/tests/FileAPI/progress-manual.html create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/Determining-Encoding.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/FileReader-event-handler-attributes.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/FileReader-multiple-reads.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_abort.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_error.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_events.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_file-manual.html create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_file_img-manual.html create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsArrayBuffer.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsBinaryString.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsDataURL.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsText.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_readystate.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/filereader_result.any.js create mode 100644 testing/web-platform/tests/FileAPI/reading-data-section/support/blue-100x100.png create mode 100644 testing/web-platform/tests/FileAPI/support/Blob.js create mode 100644 testing/web-platform/tests/FileAPI/support/document-domain-setter.sub.html create mode 100644 testing/web-platform/tests/FileAPI/support/empty-document.html create mode 100644 testing/web-platform/tests/FileAPI/support/historical-serviceworker.js create mode 100644 testing/web-platform/tests/FileAPI/support/incumbent.sub.html create mode 100644 testing/web-platform/tests/FileAPI/support/send-file-form-helper.js create mode 100644 testing/web-platform/tests/FileAPI/support/send-file-formdata-helper.js create mode 100644 testing/web-platform/tests/FileAPI/support/upload.txt create mode 100644 testing/web-platform/tests/FileAPI/support/url-origin.html create mode 100644 testing/web-platform/tests/FileAPI/unicode.html create mode 100644 testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html create mode 100644 testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html create mode 100644 testing/web-platform/tests/FileAPI/url/resources/create-helper.html create mode 100644 testing/web-platform/tests/FileAPI/url/resources/create-helper.js create mode 100644 testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js create mode 100644 testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html create mode 100644 testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js create mode 100644 testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html create mode 100644 testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html create mode 100644 testing/web-platform/tests/FileAPI/url/url-charset.window.js create mode 100644 testing/web-platform/tests/FileAPI/url/url-format.any.js create mode 100644 testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js create mode 100644 testing/web-platform/tests/FileAPI/url/url-in-tags.window.js create mode 100644 testing/web-platform/tests/FileAPI/url/url-lifetime.html create mode 100644 testing/web-platform/tests/FileAPI/url/url-reload.window.js create mode 100644 testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js create mode 100644 testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js create mode 100644 testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html create mode 100644 testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html create mode 100644 testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html create mode 100644 testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html (limited to 'testing/web-platform/tests/FileAPI') diff --git a/testing/web-platform/tests/FileAPI/Blob-methods-from-detached-frame.html b/testing/web-platform/tests/FileAPI/Blob-methods-from-detached-frame.html new file mode 100644 index 0000000000..37efd5ed20 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/Blob-methods-from-detached-frame.html @@ -0,0 +1,59 @@ + + +Blob methods from detached frame work as expected + + + + + + diff --git a/testing/web-platform/tests/FileAPI/BlobURL/cross-partition.tentative.https.html b/testing/web-platform/tests/FileAPI/BlobURL/cross-partition.tentative.https.html new file mode 100644 index 0000000000..c75ce07d05 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/BlobURL/cross-partition.tentative.https.html @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/BlobURL/support/file_test2.txt b/testing/web-platform/tests/FileAPI/BlobURL/support/file_test2.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testing/web-platform/tests/FileAPI/BlobURL/test2-manual.html b/testing/web-platform/tests/FileAPI/BlobURL/test2-manual.html new file mode 100644 index 0000000000..07fb27ef8a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/BlobURL/test2-manual.html @@ -0,0 +1,62 @@ + + + + + Blob and File reference URL Test(2) + + + + + + +
+
+
+ +
+

Test steps:

+
    +
  1. Download the file.
  2. +
  3. Select the file in the file inputbox.
  4. +
  5. Delete the file.
  6. +
  7. Click the 'start' button.
  8. +
+
+ +
+ + + + diff --git a/testing/web-platform/tests/FileAPI/FileReader/progress_event_bubbles_cancelable.html b/testing/web-platform/tests/FileAPI/FileReader/progress_event_bubbles_cancelable.html new file mode 100644 index 0000000000..6a03243f93 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/FileReader/progress_event_bubbles_cancelable.html @@ -0,0 +1,33 @@ + + +File API Test: Progress Event - bubbles, cancelable + + + + +
+ + diff --git a/testing/web-platform/tests/FileAPI/FileReader/support/file_test1.txt b/testing/web-platform/tests/FileAPI/FileReader/support/file_test1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testing/web-platform/tests/FileAPI/FileReader/test_errors-manual.html b/testing/web-platform/tests/FileAPI/FileReader/test_errors-manual.html new file mode 100644 index 0000000000..b8c3f84d2b --- /dev/null +++ b/testing/web-platform/tests/FileAPI/FileReader/test_errors-manual.html @@ -0,0 +1,72 @@ + + + + + FileReader Errors Test + + + + + + +
+
+
+ +
+

Test steps:

+
    +
  1. Download the file.
  2. +
  3. Select the file in the file inputbox.
  4. +
  5. Delete the file.
  6. +
  7. Click the 'start' button.
  8. +
+
+ +
+ + + + diff --git a/testing/web-platform/tests/FileAPI/FileReader/test_notreadableerrors-manual.html b/testing/web-platform/tests/FileAPI/FileReader/test_notreadableerrors-manual.html new file mode 100644 index 0000000000..46d73598a0 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/FileReader/test_notreadableerrors-manual.html @@ -0,0 +1,42 @@ + + +FileReader NotReadableError Test + + + + +
+
+
+ +
+

Test steps:

+
    +
  1. Download the file.
  2. +
  3. Select the file in the file inputbox.
  4. +
  5. Delete the file's readable permission.
  6. +
  7. Click the 'start' button.
  8. +
+
+ + + diff --git a/testing/web-platform/tests/FileAPI/FileReader/test_securityerrors-manual.html b/testing/web-platform/tests/FileAPI/FileReader/test_securityerrors-manual.html new file mode 100644 index 0000000000..add93ed69d --- /dev/null +++ b/testing/web-platform/tests/FileAPI/FileReader/test_securityerrors-manual.html @@ -0,0 +1,40 @@ + + +FileReader SecurityError Test + + + + +
+
+
+ +
+

Test steps:

+
    +
  1. Select a system sensitive file (e.g. files in /usr/bin, password files, + and other native operating system executables) in the file inputbox.
  2. +
  3. Click the 'start' button.
  4. +
+
+ + diff --git a/testing/web-platform/tests/FileAPI/FileReader/workers.html b/testing/web-platform/tests/FileAPI/FileReader/workers.html new file mode 100644 index 0000000000..8e114eeaf8 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/FileReader/workers.html @@ -0,0 +1,27 @@ + + + + + diff --git a/testing/web-platform/tests/FileAPI/FileReaderSync.worker.js b/testing/web-platform/tests/FileAPI/FileReaderSync.worker.js new file mode 100644 index 0000000000..3d7a0222f3 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/FileReaderSync.worker.js @@ -0,0 +1,56 @@ +importScripts("/resources/testharness.js"); + +var blob, empty_blob, readerSync; +setup(() => { + readerSync = new FileReaderSync(); + blob = new Blob(["test"]); + empty_blob = new Blob(); +}); + +test(() => { + assert_true(readerSync instanceof FileReaderSync); +}, "Interface"); + +test(() => { + var text = readerSync.readAsText(blob); + assert_equals(text, "test"); +}, "readAsText"); + +test(() => { + var text = readerSync.readAsText(empty_blob); + assert_equals(text, ""); +}, "readAsText with empty blob"); + +test(() => { + var data = readerSync.readAsDataURL(blob); + assert_equals(data.indexOf("data:"), 0); +}, "readAsDataURL"); + +test(() => { + var data = readerSync.readAsDataURL(empty_blob); + assert_equals(data.indexOf("data:"), 0); +}, "readAsDataURL with empty blob"); + +test(() => { + var data = readerSync.readAsBinaryString(blob); + assert_equals(data, "test"); +}, "readAsBinaryString"); + +test(() => { + var data = readerSync.readAsBinaryString(empty_blob); + assert_equals(data, ""); +}, "readAsBinaryString with empty blob"); + +test(() => { + var data = readerSync.readAsArrayBuffer(blob); + assert_true(data instanceof ArrayBuffer); + assert_equals(data.byteLength, "test".length); +}, "readAsArrayBuffer"); + +test(() => { + var data = readerSync.readAsArrayBuffer(empty_blob); + assert_true(data instanceof ArrayBuffer); + assert_equals(data.byteLength, 0); +}, "readAsArrayBuffer with empty blob"); + +done(); diff --git a/testing/web-platform/tests/FileAPI/META.yml b/testing/web-platform/tests/FileAPI/META.yml new file mode 100644 index 0000000000..506a59fec1 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/META.yml @@ -0,0 +1,6 @@ +spec: https://w3c.github.io/FileAPI/ +suggested_reviewers: + - inexorabletash + - zqzhang + - jdm + - mkruisselbrink diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-array-buffer.any.js b/testing/web-platform/tests/FileAPI/blob/Blob-array-buffer.any.js new file mode 100644 index 0000000000..2310646e5f --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-array-buffer.any.js @@ -0,0 +1,45 @@ +// META: title=Blob Array Buffer +// META: script=../support/Blob.js +'use strict'; + +promise_test(async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + assert_true(array_buffer instanceof ArrayBuffer); + assert_equals_typed_array(new Uint8Array(array_buffer), input_arr); +}, "Blob.arrayBuffer()") + +promise_test(async () => { + const input_arr = new TextEncoder().encode(""); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + assert_true(array_buffer instanceof ArrayBuffer); + assert_equals_typed_array(new Uint8Array(array_buffer), input_arr); +}, "Blob.arrayBuffer() empty Blob data") + +promise_test(async () => { + const input_arr = new TextEncoder().encode("\u08B8\u000a"); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + assert_equals_typed_array(new Uint8Array(array_buffer), input_arr); +}, "Blob.arrayBuffer() non-ascii input") + +promise_test(async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const array_buffer = await blob.arrayBuffer(); + assert_equals_typed_array(new Uint8Array(array_buffer), typed_arr); +}, "Blob.arrayBuffer() non-unicode input") + +promise_test(async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const array_buffer_results = await Promise.all([blob.arrayBuffer(), + blob.arrayBuffer(), blob.arrayBuffer()]); + for (let array_buffer of array_buffer_results) { + assert_true(array_buffer instanceof ArrayBuffer); + assert_equals_typed_array(new Uint8Array(array_buffer), input_arr); + } +}, "Blob.arrayBuffer() concurrent reads") diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-constructor-dom.window.js b/testing/web-platform/tests/FileAPI/blob/Blob-constructor-dom.window.js new file mode 100644 index 0000000000..4fd4a43ec4 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-constructor-dom.window.js @@ -0,0 +1,53 @@ +// META: title=Blob constructor +// META: script=../support/Blob.js +'use strict'; + +var test_error = { + name: "test", + message: "test error", +}; + +test(function() { + var args = [ + document.createElement("div"), + window, + ]; + args.forEach(function(arg) { + assert_throws_js(TypeError, function() { + new Blob(arg); + }, "Should throw for argument " + format_value(arg) + "."); + }); +}, "Passing platform objects for blobParts should throw a TypeError."); + +test(function() { + var element = document.createElement("div"); + element.appendChild(document.createElement("div")); + element.appendChild(document.createElement("p")); + var list = element.children; + Object.defineProperty(list, "length", { + get: function() { throw test_error; } + }); + assert_throws_exactly(test_error, function() { + new Blob(list); + }); +}, "A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)"); + +test_blob(function() { + var select = document.createElement("select"); + select.appendChild(document.createElement("option")); + return new Blob(select); +}, { + expected: "[object HTMLOptionElement]", + type: "", + desc: "Passing an platform object that supports indexed properties as the blobParts array should work (select)." +}); + +test_blob(function() { + var elm = document.createElement("div"); + elm.setAttribute("foo", "bar"); + return new Blob(elm.attributes); +}, { + expected: "[object Attr]", + type: "", + desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)." +}); \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-constructor-endings.html b/testing/web-platform/tests/FileAPI/blob/Blob-constructor-endings.html new file mode 100644 index 0000000000..04edd2a303 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-constructor-endings.html @@ -0,0 +1,104 @@ + + +Blob constructor: endings option + + + + diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-constructor.any.js b/testing/web-platform/tests/FileAPI/blob/Blob-constructor.any.js new file mode 100644 index 0000000000..d16f760cae --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-constructor.any.js @@ -0,0 +1,468 @@ +// META: title=Blob constructor +// META: script=../support/Blob.js +'use strict'; + +test(function() { + assert_true("Blob" in globalThis, "globalThis should have a Blob property."); + assert_equals(Blob.length, 0, "Blob.length should be 0."); + assert_true(Blob instanceof Function, "Blob should be a function."); +}, "Blob interface object"); + +// Step 1. +test(function() { + var blob = new Blob(); + assert_true(blob instanceof Blob); + assert_equals(String(blob), '[object Blob]'); + assert_equals(blob.size, 0); + assert_equals(blob.type, ""); +}, "Blob constructor with no arguments"); +test(function() { + assert_throws_js(TypeError, function() { var blob = Blob(); }); +}, "Blob constructor with no arguments, without 'new'"); +test(function() { + var blob = new Blob; + assert_true(blob instanceof Blob); + assert_equals(blob.size, 0); + assert_equals(blob.type, ""); +}, "Blob constructor without brackets"); +test(function() { + var blob = new Blob(undefined); + assert_true(blob instanceof Blob); + assert_equals(String(blob), '[object Blob]'); + assert_equals(blob.size, 0); + assert_equals(blob.type, ""); +}, "Blob constructor with undefined as first argument"); + +// blobParts argument (WebIDL). +test(function() { + var args = [ + null, + true, + false, + 0, + 1, + 1.5, + "FAIL", + new Date(), + new RegExp(), + {}, + { 0: "FAIL", length: 1 }, + ]; + args.forEach(function(arg) { + assert_throws_js(TypeError, function() { + new Blob(arg); + }, "Should throw for argument " + format_value(arg) + "."); + }); +}, "Passing non-objects, Dates and RegExps for blobParts should throw a TypeError."); + +test_blob(function() { + return new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + }); +}, { + expected: "", + type: "", + desc: "A plain object with @@iterator should be treated as a sequence for the blobParts argument." +}); +test(t => { + const blob = new Blob({ + [Symbol.iterator]() { + var i = 0; + return {next: () => [ + {done:false, value:'ab'}, + {done:false, value:'cde'}, + {done:true} + ][i++] + }; + } + }); + assert_equals(blob.size, 5, 'Custom @@iterator should be treated as a sequence'); +}, "A plain object with custom @@iterator should be treated as a sequence for the blobParts argument."); +test_blob(function() { + return new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + 0: "PASS", + length: 1 + }); +}, { + expected: "PASS", + type: "", + desc: "A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument." +}); +test_blob(function() { + return new Blob(new String("xyz")); +}, { + expected: "xyz", + type: "", + desc: "A String object should be treated as a sequence for the blobParts argument." +}); +test_blob(function() { + return new Blob(new Uint8Array([1, 2, 3])); +}, { + expected: "123", + type: "", + desc: "A Uint8Array object should be treated as a sequence for the blobParts argument." +}); + +var test_error = { + name: "test", + message: "test error", +}; + +test(function() { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + get length() { throw test_error; } + }; + assert_throws_exactly(test_error, function() { + new Blob(obj); + }); +}, "The length getter should be invoked and any exceptions should be propagated."); + +test(function() { + assert_throws_exactly(test_error, function() { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + length: { + valueOf: null, + toString: function() { throw test_error; } + } + }; + new Blob(obj); + }); + assert_throws_exactly(test_error, function() { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + length: { valueOf: function() { throw test_error; } } + }; + new Blob(obj); + }); +}, "ToUint32 should be applied to the length and any exceptions should be propagated."); + +test(function() { + var received = []; + var obj = { + get [Symbol.iterator]() { + received.push("Symbol.iterator"); + return Array.prototype[Symbol.iterator]; + }, + get length() { + received.push("length getter"); + return { + valueOf: function() { + received.push("length valueOf"); + return 3; + } + }; + }, + get 0() { + received.push("0 getter"); + return { + toString: function() { + received.push("0 toString"); + return "a"; + } + }; + }, + get 1() { + received.push("1 getter"); + throw test_error; + }, + get 2() { + received.push("2 getter"); + assert_unreached("Should not call the getter for 2 if the getter for 1 threw."); + } + }; + assert_throws_exactly(test_error, function() { + new Blob(obj); + }); + assert_array_equals(received, [ + "Symbol.iterator", + "length getter", + "length valueOf", + "0 getter", + "0 toString", + "length getter", + "length valueOf", + "1 getter", + ]); +}, "Getters and value conversions should happen in order until an exception is thrown."); + +// XXX should add tests edge cases of ToLength(length) + +test(function() { + assert_throws_exactly(test_error, function() { + new Blob([{ toString: function() { throw test_error; } }]); + }, "Throwing toString"); + assert_throws_exactly(test_error, function() { + new Blob([{ toString: undefined, valueOf: function() { throw test_error; } }]); + }, "Throwing valueOf"); + assert_throws_exactly(test_error, function() { + new Blob([{ + toString: function() { throw test_error; }, + valueOf: function() { assert_unreached("Should not call valueOf if toString is present."); } + }]); + }, "Throwing toString and valueOf"); + assert_throws_js(TypeError, function() { + new Blob([{toString: null, valueOf: null}]); + }, "Null toString and valueOf"); +}, "ToString should be called on elements of the blobParts array and any exceptions should be propagated."); + +test_blob(function() { + var arr = [ + { toString: function() { arr.pop(); return "PASS"; } }, + { toString: function() { assert_unreached("Should have removed the second element of the array rather than called toString() on it."); } } + ]; + return new Blob(arr); +}, { + expected: "PASS", + type: "", + desc: "Changes to the blobParts array should be reflected in the returned Blob (pop)." +}); + +test_blob(function() { + var arr = [ + { + toString: function() { + if (arr.length === 3) { + return "A"; + } + arr.unshift({ + toString: function() { + assert_unreached("Should only access index 0 once."); + } + }); + return "P"; + } + }, + { + toString: function() { + return "SS"; + } + } + ]; + return new Blob(arr); +}, { + expected: "PASS", + type: "", + desc: "Changes to the blobParts array should be reflected in the returned Blob (unshift)." +}); + +test_blob(function() { + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17652 + return new Blob([ + null, + undefined, + true, + false, + 0, + 1, + new String("stringobject"), + [], + ['x', 'y'], + {}, + { 0: "FAIL", length: 1 }, + { toString: function() { return "stringA"; } }, + { toString: undefined, valueOf: function() { return "stringB"; } }, + { valueOf: function() { assert_unreached("Should not call valueOf if toString is present on the prototype."); } } + ]); +}, { + expected: "nullundefinedtruefalse01stringobjectx,y[object Object][object Object]stringAstringB[object Object]", + type: "", + desc: "ToString should be called on elements of the blobParts array." +}); + +test_blob(function() { + return new Blob([ + new ArrayBuffer(8) + ]); +}, { + expected: "\0\0\0\0\0\0\0\0", + type: "", + desc: "ArrayBuffer elements of the blobParts array should be supported." +}); + +test_blob(function() { + return new Blob([ + new Uint8Array([0x50, 0x41, 0x53, 0x53]), + new Int8Array([0x50, 0x41, 0x53, 0x53]), + new Uint16Array([0x4150, 0x5353]), + new Int16Array([0x4150, 0x5353]), + new Uint32Array([0x53534150]), + new Int32Array([0x53534150]), + new Float32Array([0xD341500000]) + ]); +}, { + expected: "PASSPASSPASSPASSPASSPASSPASS", + type: "", + desc: "Passing typed arrays as elements of the blobParts array should work." +}); +test_blob(function() { + return new Blob([ + // 0x535 3415053534150 + // 0x535 = 0b010100110101 -> Sign = +, Exponent = 1333 - 1023 = 310 + // 0x13415053534150 * 2**(-52) + // ==> 0x13415053534150 * 2**258 = 2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680 + new Float64Array([2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680]) + ]); +}, { + expected: "PASSPASS", + type: "", + desc: "Passing a Float64Array as element of the blobParts array should work." +}); + +test_blob(function() { + return new Blob([ + new BigInt64Array([BigInt("0x5353415053534150")]), + new BigUint64Array([BigInt("0x5353415053534150")]) + ]); +}, { + expected: "PASSPASSPASSPASS", + type: "", + desc: "Passing BigInt typed arrays as elements of the blobParts array should work." +}); + +var t_ports = async_test("Passing a FrozenArray as the blobParts array should work (FrozenArray)."); +t_ports.step(function() { + var channel = new MessageChannel(); + channel.port2.onmessage = this.step_func(function(e) { + var b_ports = new Blob(e.ports); + assert_equals(b_ports.size, "[object MessagePort]".length); + this.done(); + }); + var channel2 = new MessageChannel(); + channel.port1.postMessage('', [channel2.port1]); +}); + +test_blob(function() { + var blob = new Blob(['foo']); + return new Blob([blob, blob]); +}, { + expected: "foofoo", + type: "", + desc: "Array with two blobs" +}); + +test_blob_binary(function() { + var view = new Uint8Array([0, 255, 0]); + return new Blob([view.buffer, view.buffer]); +}, { + expected: [0, 255, 0, 0, 255, 0], + type: "", + desc: "Array with two buffers" +}); + +test_blob_binary(function() { + var view = new Uint8Array([0, 255, 0, 4]); + var blob = new Blob([view, view]); + assert_equals(blob.size, 8); + var view1 = new Uint16Array(view.buffer, 2); + return new Blob([view1, view.buffer, view1]); +}, { + expected: [0, 4, 0, 255, 0, 4, 0, 4], + type: "", + desc: "Array with two bufferviews" +}); + +test_blob(function() { + var view = new Uint8Array([0]); + var blob = new Blob(["fo"]); + return new Blob([view.buffer, blob, "foo"]); +}, { + expected: "\0fofoo", + type: "", + desc: "Array with mixed types" +}); + +test(function() { + const accessed = []; + const stringified = []; + + new Blob([], { + get type() { accessed.push('type'); }, + get endings() { accessed.push('endings'); } + }); + new Blob([], { + type: { toString: () => { stringified.push('type'); return ''; } }, + endings: { toString: () => { stringified.push('endings'); return 'transparent'; } } + }); + assert_array_equals(accessed, ['endings', 'type']); + assert_array_equals(stringified, ['endings', 'type']); +}, "options properties should be accessed in lexicographic order."); + +test(function() { + assert_throws_exactly(test_error, function() { + new Blob( + [{ toString: function() { throw test_error } }], + { + get type() { assert_unreached("type getter should not be called."); } + } + ); + }); +}, "Arguments should be evaluated from left to right."); + +[ + null, + undefined, + {}, + { unrecognized: true }, + /regex/, + function() {} +].forEach(function(arg, idx) { + test_blob(function() { + return new Blob([], arg); + }, { + expected: "", + type: "", + desc: "Passing " + format_value(arg) + " (index " + idx + ") for options should use the defaults." + }); + test_blob(function() { + return new Blob(["\na\r\nb\n\rc\r"], arg); + }, { + expected: "\na\r\nb\n\rc\r", + type: "", + desc: "Passing " + format_value(arg) + " (index " + idx + ") for options should use the defaults (with newlines)." + }); +}); + +[ + 123, + 123.4, + true, + 'abc' +].forEach(arg => { + test(t => { + assert_throws_js(TypeError, () => new Blob([], arg), + 'Blob constructor should throw with invalid property bag'); + }, `Passing ${JSON.stringify(arg)} for options should throw`); +}); + +var type_tests = [ + // blobParts, type, expected type + [[], '', ''], + [[], 'a', 'a'], + [[], 'A', 'a'], + [[], 'text/html', 'text/html'], + [[], 'TEXT/HTML', 'text/html'], + [[], 'text/plain;charset=utf-8', 'text/plain;charset=utf-8'], + [[], '\u00E5', ''], + [[], '\uD801\uDC7E', ''], // U+1047E + [[], ' image/gif ', ' image/gif '], + [[], '\timage/gif\t', ''], + [[], 'image/gif;\u007f', ''], + [[], '\u0130mage/gif', ''], // uppercase i with dot + [[], '\u0131mage/gif', ''], // lowercase dotless i + [[], 'image/gif\u0000', ''], + // check that type isn't changed based on sniffing + [[0x3C, 0x48, 0x54, 0x4D, 0x4C, 0x3E], 'unknown/unknown', 'unknown/unknown'], // "" + [[0x00, 0xFF], 'text/plain', 'text/plain'], + [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], 'image/png', 'image/png'], // "GIF89a" +]; + +type_tests.forEach(function(t) { + test(function() { + var arr = new Uint8Array([t[0]]).buffer; + var b = new Blob([arr], {type:t[1]}); + assert_equals(b.type, t[2]); + }, "Blob with type " + format_value(t[1])); +}); diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-in-worker.worker.js b/testing/web-platform/tests/FileAPI/blob/Blob-in-worker.worker.js new file mode 100644 index 0000000000..a0ca84551d --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-in-worker.worker.js @@ -0,0 +1,9 @@ +importScripts("/resources/testharness.js"); + +promise_test(async () => { + const data = "TEST"; + const blob = new Blob([data], {type: "text/plain"}); + assert_equals(await blob.text(), data); +}, 'Create Blob in Worker'); + +done(); diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-slice-overflow.any.js b/testing/web-platform/tests/FileAPI/blob/Blob-slice-overflow.any.js new file mode 100644 index 0000000000..388fd9282c --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-slice-overflow.any.js @@ -0,0 +1,32 @@ +// META: title=Blob slice overflow +'use strict'; + +var text = ''; + +for (var i = 0; i < 2000; ++i) { + text += 'A'; +} + +test(function() { + var blob = new Blob([text]); + var sliceBlob = blob.slice(-1, blob.size); + assert_equals(sliceBlob.size, 1, "Blob slice size"); +}, "slice start is negative, relativeStart will be max((size + start), 0)"); + +test(function() { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size + 1, blob.size); + assert_equals(sliceBlob.size, 0, "Blob slice size"); +}, "slice start is greater than blob size, relativeStart will be min(start, size)"); + +test(function() { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size - 2, -1); + assert_equals(sliceBlob.size, 1, "Blob slice size"); +}, "slice end is negative, relativeEnd will be max((size + end), 0)"); + +test(function() { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size - 2, blob.size + 999); + assert_equals(sliceBlob.size, 2, "Blob slice size"); +}, "slice end is greater than blob size, relativeEnd will be min(end, size)"); diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-slice.any.js b/testing/web-platform/tests/FileAPI/blob/Blob-slice.any.js new file mode 100644 index 0000000000..1f85d44d26 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-slice.any.js @@ -0,0 +1,231 @@ +// META: title=Blob slice +// META: script=../support/Blob.js +'use strict'; + +test_blob(function() { + var blobTemp = new Blob(["PASS"]); + return blobTemp.slice(); +}, { + expected: "PASS", + type: "", + desc: "no-argument Blob slice" +}); + +test(function() { + var blob1, blob2; + + test_blob(function() { + return blob1 = new Blob(["squiggle"]); + }, { + expected: "squiggle", + type: "", + desc: "blob1." + }); + + test_blob(function() { + return blob2 = new Blob(["steak"], {type: "content/type"}); + }, { + expected: "steak", + type: "content/type", + desc: "blob2." + }); + + test_blob(function() { + return new Blob().slice(0,0,null); + }, { + expected: "", + type: "null", + desc: "null type Blob slice" + }); + + test_blob(function() { + return new Blob().slice(0,0,undefined); + }, { + expected: "", + type: "", + desc: "undefined type Blob slice" + }); + + test_blob(function() { + return new Blob().slice(0,0); + }, { + expected: "", + type: "", + desc: "no type Blob slice" + }); + + var arrayBuffer = new ArrayBuffer(16); + var int8View = new Int8Array(arrayBuffer); + for (var i = 0; i < 16; i++) { + int8View[i] = i + 65; + } + + var testData = [ + [ + ["PASSSTRING"], + [{start: -6, contents: "STRING"}, + {start: -12, contents: "PASSSTRING"}, + {start: 4, contents: "STRING"}, + {start: 12, contents: ""}, + {start: 0, end: -6, contents: "PASS"}, + {start: 0, end: -12, contents: ""}, + {start: 0, end: 4, contents: "PASS"}, + {start: 0, end: 12, contents: "PASSSTRING"}, + {start: 7, end: 4, contents: ""}] + ], + + // Test 3 strings + [ + ["foo", "bar", "baz"], + [{start: 0, end: 9, contents: "foobarbaz"}, + {start: 0, end: 3, contents: "foo"}, + {start: 3, end: 9, contents: "barbaz"}, + {start: 6, end: 9, contents: "baz"}, + {start: 6, end: 12, contents: "baz"}, + {start: 0, end: 9, contents: "foobarbaz"}, + {start: 0, end: 11, contents: "foobarbaz"}, + {start: 10, end: 15, contents: ""}] + ], + + // Test string, Blob, string + [ + ["foo", blob1, "baz"], + [{start: 0, end: 3, contents: "foo"}, + {start: 3, end: 11, contents: "squiggle"}, + {start: 2, end: 4, contents: "os"}, + {start: 10, end: 12, contents: "eb"}] + ], + + // Test blob, string, blob + [ + [blob1, "foo", blob1], + [{start: 0, end: 8, contents: "squiggle"}, + {start: 7, end: 9, contents: "ef"}, + {start: 10, end: 12, contents: "os"}, + {start: 1, end: 4, contents: "qui"}, + {start: 12, end: 15, contents: "qui"}, + {start: 40, end: 60, contents: ""}] + ], + + // Test blobs all the way down + [ + [blob2, blob1, blob2], + [{start: 0, end: 5, contents: "steak"}, + {start: 5, end: 13, contents: "squiggle"}, + {start: 13, end: 18, contents: "steak"}, + {start: 1, end: 3, contents: "te"}, + {start: 6, end: 10, contents: "quig"}] + ], + + // Test an ArrayBufferView + [ + [int8View, blob1, "foo"], + [{start: 0, end: 8, contents: "ABCDEFGH"}, + {start: 8, end: 18, contents: "IJKLMNOPsq"}, + {start: 17, end: 20, contents: "qui"}, + {start: 4, end: 12, contents: "EFGHIJKL"}] + ], + + // Test a partial ArrayBufferView + [ + [new Uint8Array(arrayBuffer, 3, 5), blob1, "foo"], + [{start: 0, end: 8, contents: "DEFGHsqu"}, + {start: 8, end: 18, contents: "igglefoo"}, + {start: 4, end: 12, contents: "Hsquiggl"}] + ], + + // Test type coercion of a number + [ + [3, int8View, "foo"], + [{start: 0, end: 8, contents: "3ABCDEFG"}, + {start: 8, end: 18, contents: "HIJKLMNOPf"}, + {start: 17, end: 21, contents: "foo"}, + {start: 4, end: 12, contents: "DEFGHIJK"}] + ], + + [ + [(new Uint8Array([0, 255, 0])).buffer, + new Blob(['abcd']), + 'efgh', + 'ijklmnopqrstuvwxyz'], + [{start: 1, end: 4, contents: "\uFFFD\u0000a"}, + {start: 4, end: 8, contents: "bcde"}, + {start: 8, end: 12, contents: "fghi"}, + {start: 1, end: 12, contents: "\uFFFD\u0000abcdefghi"}] + ] + ]; + + testData.forEach(function(data, i) { + var blobs = data[0]; + var tests = data[1]; + tests.forEach(function(expectations, j) { + test(function() { + var blob = new Blob(blobs); + assert_true(blob instanceof Blob); + assert_false(blob instanceof File); + + test_blob(function() { + return expectations.end === undefined + ? blob.slice(expectations.start) + : blob.slice(expectations.start, expectations.end); + }, { + expected: expectations.contents, + type: "", + desc: "Slicing test: slice (" + i + "," + j + ")." + }); + }, "Slicing test (" + i + "," + j + ")."); + }); + }); +}, "Slices"); + +var invalidTypes = [ + "\xFF", + "te\x09xt/plain", + "te\x00xt/plain", + "te\x1Fxt/plain", + "te\x7Fxt/plain" +]; +invalidTypes.forEach(function(type) { + test_blob(function() { + var blob = new Blob(["PASS"]); + return blob.slice(0, 4, type); + }, { + expected: "PASS", + type: "", + desc: "Invalid contentType (" + format_value(type) + ")" + }); +}); + +var validTypes = [ + "te(xt/plain", + "te)xt/plain", + "text/plain", + "te@xt/plain", + "te,xt/plain", + "te;xt/plain", + "te:xt/plain", + "te\\xt/plain", + "te\"xt/plain", + "te/xt/plain", + "te[xt/plain", + "te]xt/plain", + "te?xt/plain", + "te=xt/plain", + "te{xt/plain", + "te}xt/plain", + "te\x20xt/plain", + "TEXT/PLAIN", + "text/plain;charset = UTF-8", + "text/plain;charset=UTF-8" +]; +validTypes.forEach(function(type) { + test_blob(function() { + var blob = new Blob(["PASS"]); + return blob.slice(0, 4, type); + }, { + expected: "PASS", + type: type.toLowerCase(), + desc: "Valid contentType (" + format_value(type) + ")" + }); +}); diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-stream-byob-crash.html b/testing/web-platform/tests/FileAPI/blob/Blob-stream-byob-crash.html new file mode 100644 index 0000000000..5992ed1396 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-stream-byob-crash.html @@ -0,0 +1,11 @@ + + diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-stream-sync-xhr-crash.html b/testing/web-platform/tests/FileAPI/blob/Blob-stream-sync-xhr-crash.html new file mode 100644 index 0000000000..fe54fb615b --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-stream-sync-xhr-crash.html @@ -0,0 +1,13 @@ + + diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-stream.any.js b/testing/web-platform/tests/FileAPI/blob/Blob-stream.any.js new file mode 100644 index 0000000000..453144cac9 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-stream.any.js @@ -0,0 +1,94 @@ +// META: title=Blob Stream +// META: script=../support/Blob.js +// META: script=/common/gc.js +'use strict'; + +// Helper function that triggers garbage collection while reading a chunk +// if perform_gc is true. +async function read_and_gc(reader, perform_gc) { + // Passing Uint8Array for byte streams; non-byte streams will simply ignore it + const read_promise = reader.read(new Uint8Array(64)); + if (perform_gc) { + await garbageCollect(); + } + return read_promise; +} + +// Takes in a ReadableStream and reads from it until it is done, returning +// an array that contains the results of each read operation. If perform_gc +// is true, garbage collection is triggered while reading every chunk. +async function read_all_chunks(stream, { perform_gc = false, mode } = {}) { + assert_true(stream instanceof ReadableStream); + assert_true('getReader' in stream); + const reader = stream.getReader({ mode }); + + assert_true('read' in reader); + let read_value = await read_and_gc(reader, perform_gc); + + let out = []; + let i = 0; + while (!read_value.done) { + for (let val of read_value.value) { + out[i++] = val; + } + read_value = await read_and_gc(reader, perform_gc); + } + return out; +} + +promise_test(async () => { + const blob = new Blob(["PASS"]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + for (let [index, value] of chunks.entries()) { + assert_equals(value, "PASS".charCodeAt(index)); + } +}, "Blob.stream()") + +promise_test(async () => { + const blob = new Blob(); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + assert_array_equals(chunks, []); +}, "Blob.stream() empty Blob") + +promise_test(async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + assert_array_equals(chunks, input_arr); +}, "Blob.stream() non-unicode input") + +promise_test(async() => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const stream = blob.stream(); + blob = null; + await garbageCollect(); + const chunks = await read_all_chunks(stream, { perform_gc: true }); + assert_array_equals(chunks, input_arr); +}, "Blob.stream() garbage collection of blob shouldn't break stream " + + "consumption") + +promise_test(async() => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const chunksPromise = read_all_chunks(blob.stream()); + // It somehow matters to do GC here instead of doing `perform_gc: true` + await garbageCollect(); + assert_array_equals(await chunksPromise, input_arr); +}, "Blob.stream() garbage collection of stream shouldn't break stream " + + "consumption") + +promise_test(async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream, { mode: "byob" }); + assert_array_equals(chunks, input_arr); +}, "Reading Blob.stream() with BYOB reader") diff --git a/testing/web-platform/tests/FileAPI/blob/Blob-text.any.js b/testing/web-platform/tests/FileAPI/blob/Blob-text.any.js new file mode 100644 index 0000000000..d04fa97cff --- /dev/null +++ b/testing/web-platform/tests/FileAPI/blob/Blob-text.any.js @@ -0,0 +1,64 @@ +// META: title=Blob Text +// META: script=../support/Blob.js +'use strict'; + +promise_test(async () => { + const blob = new Blob(["PASS"]); + const text = await blob.text(); + assert_equals(text, "PASS"); +}, "Blob.text()") + +promise_test(async () => { + const blob = new Blob(); + const text = await blob.text(); + assert_equals(text, ""); +}, "Blob.text() empty blob data") + +promise_test(async () => { + const blob = new Blob(["P", "A", "SS"]); + const text = await blob.text(); + assert_equals(text, "PASS"); +}, "Blob.text() multi-element array in constructor") + +promise_test(async () => { + const non_unicode = "\u0061\u030A"; + const input_arr = new TextEncoder().encode(non_unicode); + const blob = new Blob([input_arr]); + const text = await blob.text(); + assert_equals(text, non_unicode); +}, "Blob.text() non-unicode") + +promise_test(async () => { + const blob = new Blob(["PASS"], { type: "text/plain;charset=utf-16le" }); + const text = await blob.text(); + assert_equals(text, "PASS"); +}, "Blob.text() different charset param in type option") + +promise_test(async () => { + const non_unicode = "\u0061\u030A"; + const input_arr = new TextEncoder().encode(non_unicode); + const blob = new Blob([input_arr], { type: "text/plain;charset=utf-16le" }); + const text = await blob.text(); + assert_equals(text, non_unicode); +}, "Blob.text() different charset param with non-ascii input") + +promise_test(async () => { + const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255]); + const blob = new Blob([input_arr]); + const text = await blob.text(); + assert_equals(text, "\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd" + + "\ufffd\ufffd\ufffd\ufffd"); +}, "Blob.text() invalid utf-8 input") + +promise_test(async () => { + const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255]); + const blob = new Blob([input_arr]); + const text_results = await Promise.all([blob.text(), blob.text(), + blob.text()]); + for (let text of text_results) { + assert_equals(text, "\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd" + + "\ufffd\ufffd\ufffd\ufffd"); + } +}, "Blob.text() concurrent reads") diff --git a/testing/web-platform/tests/FileAPI/file/File-constructor-endings.html b/testing/web-platform/tests/FileAPI/file/File-constructor-endings.html new file mode 100644 index 0000000000..1282b6c5ac --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/File-constructor-endings.html @@ -0,0 +1,104 @@ + + +File constructor: endings option + + + + diff --git a/testing/web-platform/tests/FileAPI/file/File-constructor.any.js b/testing/web-platform/tests/FileAPI/file/File-constructor.any.js new file mode 100644 index 0000000000..0b0185c40b --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/File-constructor.any.js @@ -0,0 +1,155 @@ +// META: title=File constructor + +const to_string_obj = { toString: () => 'a string' }; +const to_string_throws = { toString: () => { throw new Error('expected'); } }; + +test(function() { + assert_true("File" in globalThis, "globalThis should have a File property."); +}, "File interface object exists"); + +test(t => { + assert_throws_js(TypeError, () => new File(), + 'Bits argument is required'); + assert_throws_js(TypeError, () => new File([]), + 'Name argument is required'); +}, 'Required arguments'); + +function test_first_argument(arg1, expectedSize, testName) { + test(function() { + var file = new File(arg1, "dummy"); + assert_true(file instanceof File); + assert_equals(file.name, "dummy"); + assert_equals(file.size, expectedSize); + assert_equals(file.type, ""); + // assert_false(file.isClosed); XXX: File.isClosed doesn't seem to be implemented + assert_not_equals(file.lastModified, ""); + }, testName); +} + +test_first_argument([], 0, "empty fileBits"); +test_first_argument(["bits"], 4, "DOMString fileBits"); +test_first_argument(["𝓽𝓮𝔁𝓽"], 16, "Unicode DOMString fileBits"); +test_first_argument([new String('string object')], 13, "String object fileBits"); +test_first_argument([new Blob()], 0, "Empty Blob fileBits"); +test_first_argument([new Blob(["bits"])], 4, "Blob fileBits"); +test_first_argument([new File([], 'world.txt')], 0, "Empty File fileBits"); +test_first_argument([new File(["bits"], 'world.txt')], 4, "File fileBits"); +test_first_argument([new ArrayBuffer(8)], 8, "ArrayBuffer fileBits"); +test_first_argument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4, "Typed array fileBits"); +test_first_argument(["bits", new Blob(["bits"]), new Blob(), new Uint8Array([0x50, 0x41]), + new Uint16Array([0x5353]), new Uint32Array([0x53534150])], 16, "Various fileBits"); +test_first_argument([12], 2, "Number in fileBits"); +test_first_argument([[1,2,3]], 5, "Array in fileBits"); +test_first_argument([{}], 15, "Object in fileBits"); // "[object Object]" +if (globalThis.document !== undefined) { + test_first_argument([document.body], 24, "HTMLBodyElement in fileBits"); // "[object HTMLBodyElement]" +} +test_first_argument([to_string_obj], 8, "Object with toString in fileBits"); +test_first_argument({[Symbol.iterator]() { + let i = 0; + return {next: () => [ + {done:false, value:'ab'}, + {done:false, value:'cde'}, + {done:true} + ][i++]}; +}}, 5, 'Custom @@iterator'); + +[ + 'hello', + 0, + null +].forEach(arg => { + test(t => { + assert_throws_js(TypeError, () => new File(arg, 'world.html'), + 'Constructor should throw for invalid bits argument'); + }, `Invalid bits argument: ${JSON.stringify(arg)}`); +}); + +test(t => { + assert_throws_js(Error, () => new File([to_string_throws], 'name.txt'), + 'Constructor should propagate exceptions'); +}, 'Bits argument: object that throws'); + + +function test_second_argument(arg2, expectedFileName, testName) { + test(function() { + var file = new File(["bits"], arg2); + assert_true(file instanceof File); + assert_equals(file.name, expectedFileName); + }, testName); +} + +test_second_argument("dummy", "dummy", "Using fileName"); +test_second_argument("dummy/foo", "dummy/foo", + "No replacement when using special character in fileName"); +test_second_argument(null, "null", "Using null fileName"); +test_second_argument(1, "1", "Using number fileName"); +test_second_argument('', '', "Using empty string fileName"); +if (globalThis.document !== undefined) { + test_second_argument(document.body, '[object HTMLBodyElement]', "Using object fileName"); +} + +// testing the third argument +[ + {type: 'text/plain', expected: 'text/plain'}, + {type: 'text/plain;charset=UTF-8', expected: 'text/plain;charset=utf-8'}, + {type: 'TEXT/PLAIN', expected: 'text/plain'}, + {type: '𝓽𝓮𝔁𝓽/𝔭𝔩𝔞𝔦𝔫', expected: ''}, + {type: 'ascii/nonprintable\u001F', expected: ''}, + {type: 'ascii/nonprintable\u007F', expected: ''}, + {type: 'nonascii\u00EE', expected: ''}, + {type: 'nonascii\u1234', expected: ''}, + {type: 'nonparsable', expected: 'nonparsable'} +].forEach(testCase => { + test(t => { + var file = new File(["bits"], "dummy", { type: testCase.type}); + assert_true(file instanceof File); + assert_equals(file.type, testCase.expected); + }, `Using type in File constructor: ${testCase.type}`); +}); +test(function() { + var file = new File(["bits"], "dummy", { lastModified: 42 }); + assert_true(file instanceof File); + assert_equals(file.lastModified, 42); +}, "Using lastModified"); +test(function() { + var file = new File(["bits"], "dummy", { name: "foo" }); + assert_true(file instanceof File); + assert_equals(file.name, "dummy"); +}, "Misusing name"); +test(function() { + var file = new File(["bits"], "dummy", { unknownKey: "value" }); + assert_true(file instanceof File); + assert_equals(file.name, "dummy"); +}, "Unknown properties are ignored"); + +[ + 123, + 123.4, + true, + 'abc' +].forEach(arg => { + test(t => { + assert_throws_js(TypeError, () => new File(['bits'], 'name.txt', arg), + 'Constructor should throw for invalid property bag type'); + }, `Invalid property bag: ${JSON.stringify(arg)}`); +}); + +[ + null, + undefined, + [1,2,3], + /regex/, + function() {} +].forEach(arg => { + test(t => { + assert_equals(new File(['bits'], 'name.txt', arg).size, 4, + 'Constructor should accept object-ish property bag type'); + }, `Unusual but valid property bag: ${arg}`); +}); + +test(t => { + assert_throws_js(Error, + () => new File(['bits'], 'name.txt', {type: to_string_throws}), + 'Constructor should propagate exceptions'); +}, 'Property bag propagates exceptions'); diff --git a/testing/web-platform/tests/FileAPI/file/Worker-read-file-constructor.worker.js b/testing/web-platform/tests/FileAPI/file/Worker-read-file-constructor.worker.js new file mode 100644 index 0000000000..4e003b3c95 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/Worker-read-file-constructor.worker.js @@ -0,0 +1,15 @@ +importScripts("/resources/testharness.js"); + +async_test(function() { + var file = new File(["bits"], "dummy", { 'type': 'text/plain', lastModified: 42 }); + var reader = new FileReader(); + reader.onload = this.step_func_done(function() { + assert_equals(file.name, "dummy", "file name"); + assert_equals(reader.result, "bits", "file content"); + assert_equals(file.lastModified, 42, "file lastModified"); + }); + reader.onerror = this.unreached_func("Unexpected error event"); + reader.readAsText(file); +}, "FileReader in Worker"); + +done(); diff --git a/testing/web-platform/tests/FileAPI/file/resources/echo-content-escaped.py b/testing/web-platform/tests/FileAPI/file/resources/echo-content-escaped.py new file mode 100644 index 0000000000..5370e1e46a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/resources/echo-content-escaped.py @@ -0,0 +1,26 @@ +from wptserve.utils import isomorphic_encode + +# Outputs the request body, with controls and non-ASCII bytes escaped +# (b"\n" becomes b"\\x0a"), and with backslashes doubled. +# As a convenience, CRLF newlines are left as is. + +def escape_byte(byte): + # Convert int byte into a single-char binary string. + byte = bytes([byte]) + if b"\0" <= byte <= b"\x1F" or byte >= b"\x7F": + return b"\\x%02x" % ord(byte) + if byte == b"\\": + return b"\\\\" + return byte + +def main(request, response): + + headers = [(b"X-Request-Method", isomorphic_encode(request.method)), + (b"X-Request-Content-Length", request.headers.get(b"Content-Length", b"NO")), + (b"X-Request-Content-Type", request.headers.get(b"Content-Type", b"NO")), + # Avoid any kind of content sniffing on the response. + (b"Content-Type", b"text/plain; charset=UTF-8")] + + content = b"".join(map(escape_byte, request.body)).replace(b"\\x0d\\x0a", b"\r\n") + + return headers, content diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form-controls.html b/testing/web-platform/tests/FileAPI/file/send-file-form-controls.html new file mode 100644 index 0000000000..6347065bca --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form-controls.html @@ -0,0 +1,113 @@ + + +Upload files named using controls + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form-iso-2022-jp.html b/testing/web-platform/tests/FileAPI/file/send-file-form-iso-2022-jp.html new file mode 100644 index 0000000000..c931c9be3a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form-iso-2022-jp.html @@ -0,0 +1,65 @@ + + + +Upload files in ISO-2022-JP form + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form-punctuation.html b/testing/web-platform/tests/FileAPI/file/send-file-form-punctuation.html new file mode 100644 index 0000000000..a6568e2e56 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form-punctuation.html @@ -0,0 +1,226 @@ + + +Upload files named using punctuation + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form-utf-8.html b/testing/web-platform/tests/FileAPI/file/send-file-form-utf-8.html new file mode 100644 index 0000000000..1be44f4f4d --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form-utf-8.html @@ -0,0 +1,62 @@ + + +Upload files in UTF-8 form + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form-windows-1252.html b/testing/web-platform/tests/FileAPI/file/send-file-form-windows-1252.html new file mode 100644 index 0000000000..21b219ffd2 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form-windows-1252.html @@ -0,0 +1,62 @@ + + +Upload files in Windows-1252 form + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form-x-user-defined.html b/testing/web-platform/tests/FileAPI/file/send-file-form-x-user-defined.html new file mode 100644 index 0000000000..8d6605d86d --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form-x-user-defined.html @@ -0,0 +1,63 @@ + + +Upload files in x-user-defined form + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-form.html b/testing/web-platform/tests/FileAPI/file/send-file-form.html new file mode 100644 index 0000000000..baa8d4286c --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-form.html @@ -0,0 +1,25 @@ + + +Upload ASCII-named file in UTF-8 form + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/file/send-file-formdata-controls.any.js b/testing/web-platform/tests/FileAPI/file/send-file-formdata-controls.any.js new file mode 100644 index 0000000000..e95d3aada4 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-formdata-controls.any.js @@ -0,0 +1,69 @@ +// META: title=FormData: FormData: Upload files named using controls +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-NUL-[\0].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-BS-[\b].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-VT-[\v].txt", + }); + + // These have characters that undergo processing in name=, + // filename=, and/or value; formDataPostFileUploadTest postprocesses + // expectedEncodedBaseName for these internally. + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LF-[\n].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-CR-[\r].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-HT-[\t].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-FF-[\f].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt", + }); + + // The rest should be passed through unmodified: + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt", + }); diff --git a/testing/web-platform/tests/FileAPI/file/send-file-formdata-punctuation.any.js b/testing/web-platform/tests/FileAPI/file/send-file-formdata-punctuation.any.js new file mode 100644 index 0000000000..987dba39af --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-formdata-punctuation.any.js @@ -0,0 +1,144 @@ +// META: title=FormData: FormData: Upload files named using punctuation +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + // These have characters that undergo processing in name=, + // filename=, and/or value; formDataPostFileUploadTest postprocesses + // expectedEncodedBaseName for these internally. + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: '"file-for-upload-in-form-double-quoted.txt"', + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt", + }); + + // The rest should be passed through unmodified: + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-COMMA-[,].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-COLON-[:].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-TILDE-[~].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "'file-for-upload-in-form-single-quoted.txt'", + }); diff --git a/testing/web-platform/tests/FileAPI/file/send-file-formdata-utf-8.any.js b/testing/web-platform/tests/FileAPI/file/send-file-formdata-utf-8.any.js new file mode 100644 index 0000000000..b8bd74c717 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-formdata-utf-8.any.js @@ -0,0 +1,33 @@ +// META: title=FormData: FormData: Upload files in UTF-8 fetch() +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "x-user-defined", + fileBaseName: "file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "windows-1252", + fileBaseName: "file-for-upload-in-form-☺😂.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "JIS X 0201 and JIS X 0208", + fileBaseName: "file-for-upload-in-form-★星★.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "Unicode", + fileBaseName: "file-for-upload-in-form-☺😂.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "Unicode", + fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`, + }); diff --git a/testing/web-platform/tests/FileAPI/file/send-file-formdata.any.js b/testing/web-platform/tests/FileAPI/file/send-file-formdata.any.js new file mode 100644 index 0000000000..e13a34828a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/file/send-file-formdata.any.js @@ -0,0 +1,8 @@ +// META: title=FormData: Upload ASCII-named file in UTF-8 form +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form.txt", + }); diff --git a/testing/web-platform/tests/FileAPI/fileReader.any.js b/testing/web-platform/tests/FileAPI/fileReader.any.js new file mode 100644 index 0000000000..2876dcb4ce --- /dev/null +++ b/testing/web-platform/tests/FileAPI/fileReader.any.js @@ -0,0 +1,59 @@ +// META: title=FileReader States + +'use strict'; + +test(function () { + assert_true( + "FileReader" in globalThis, + "globalThis should have a FileReader property.", + ); +}, "FileReader interface object"); + +test(function () { + var fileReader = new FileReader(); + assert_true(fileReader instanceof FileReader); +}, "no-argument FileReader constructor"); + +var t_abort = async_test("FileReader States -- abort"); +t_abort.step(function () { + var fileReader = new FileReader(); + assert_equals(fileReader.readyState, 0); + assert_equals(fileReader.readyState, FileReader.EMPTY); + + var blob = new Blob(); + fileReader.readAsArrayBuffer(blob); + assert_equals(fileReader.readyState, 1); + assert_equals(fileReader.readyState, FileReader.LOADING); + + fileReader.onabort = this.step_func(function (e) { + assert_equals(fileReader.readyState, 2); + assert_equals(fileReader.readyState, FileReader.DONE); + t_abort.done(); + }); + fileReader.abort(); + fileReader.onabort = this.unreached_func("abort event should fire sync"); +}); + +var t_event = async_test("FileReader States -- events"); +t_event.step(function () { + var fileReader = new FileReader(); + + var blob = new Blob(); + fileReader.readAsArrayBuffer(blob); + + fileReader.onloadstart = this.step_func(function (e) { + assert_equals(fileReader.readyState, 1); + assert_equals(fileReader.readyState, FileReader.LOADING); + }); + + fileReader.onprogress = this.step_func(function (e) { + assert_equals(fileReader.readyState, 1); + assert_equals(fileReader.readyState, FileReader.LOADING); + }); + + fileReader.onloadend = this.step_func(function (e) { + assert_equals(fileReader.readyState, 2); + assert_equals(fileReader.readyState, FileReader.DONE); + t_event.done(); + }); +}); diff --git a/testing/web-platform/tests/FileAPI/filelist-section/filelist.html b/testing/web-platform/tests/FileAPI/filelist-section/filelist.html new file mode 100644 index 0000000000..b97dcde19f --- /dev/null +++ b/testing/web-platform/tests/FileAPI/filelist-section/filelist.html @@ -0,0 +1,57 @@ + + + + + FileAPI Test: filelist + + + + + + + + + +
+ +
+
+ + + + + diff --git a/testing/web-platform/tests/FileAPI/filelist-section/filelist_multiple_selected_files-manual.html b/testing/web-platform/tests/FileAPI/filelist-section/filelist_multiple_selected_files-manual.html new file mode 100644 index 0000000000..2efaa059fa --- /dev/null +++ b/testing/web-platform/tests/FileAPI/filelist-section/filelist_multiple_selected_files-manual.html @@ -0,0 +1,64 @@ + + + + + FileAPI Test: filelist_multiple_selected_files + + + + + + + + + +
+ +
+
+

Test steps:

+
    +
  1. Download upload.txt, upload.zip to local.
  2. +
  3. Select the local two files (upload.txt, upload.zip) to run the test.
  4. +
+
+ +
+ + + + diff --git a/testing/web-platform/tests/FileAPI/filelist-section/filelist_selected_file-manual.html b/testing/web-platform/tests/FileAPI/filelist-section/filelist_selected_file-manual.html new file mode 100644 index 0000000000..966aadda61 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/filelist-section/filelist_selected_file-manual.html @@ -0,0 +1,64 @@ + + + + + FileAPI Test: filelist_selected_file + + + + + + + + + +
+ +
+
+

Test steps:

+
    +
  1. Download upload.txt to local.
  2. +
  3. Select the local upload.txt file to run the test.
  4. +
+
+ +
+ + + + diff --git a/testing/web-platform/tests/FileAPI/filelist-section/support/upload.txt b/testing/web-platform/tests/FileAPI/filelist-section/support/upload.txt new file mode 100644 index 0000000000..f45965b711 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/filelist-section/support/upload.txt @@ -0,0 +1 @@ +Hello, this is test file for file upload. diff --git a/testing/web-platform/tests/FileAPI/filelist-section/support/upload.zip b/testing/web-platform/tests/FileAPI/filelist-section/support/upload.zip new file mode 100644 index 0000000000..a933d6a949 Binary files /dev/null and b/testing/web-platform/tests/FileAPI/filelist-section/support/upload.zip differ diff --git a/testing/web-platform/tests/FileAPI/historical.https.html b/testing/web-platform/tests/FileAPI/historical.https.html new file mode 100644 index 0000000000..4f841f1763 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/historical.https.html @@ -0,0 +1,65 @@ + + + + + Historical features + + + + + +
+ + + diff --git a/testing/web-platform/tests/FileAPI/idlharness-manual.html b/testing/web-platform/tests/FileAPI/idlharness-manual.html new file mode 100644 index 0000000000..c1d8b0c714 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/idlharness-manual.html @@ -0,0 +1,45 @@ + + + + + File API manual IDL tests + + + + + + + + +

File API manual IDL tests

+ +

Either download upload.txt and select it below or select an + arbitrary local file.

+ +
+ +
+ +
+ + + + diff --git a/testing/web-platform/tests/FileAPI/idlharness.any.js b/testing/web-platform/tests/FileAPI/idlharness.any.js new file mode 100644 index 0000000000..1744242b9f --- /dev/null +++ b/testing/web-platform/tests/FileAPI/idlharness.any.js @@ -0,0 +1,19 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +'use strict'; + +// https://w3c.github.io/FileAPI/ + +idl_test( + ['FileAPI'], + ['dom', 'html', 'url'], + idl_array => { + idl_array.add_objects({ + Blob: ['new Blob(["TEST"])'], + File: ['new File(["myFileBits"], "myFileName")'], + FileReader: ['new FileReader()'] + }); + } +); diff --git a/testing/web-platform/tests/FileAPI/idlharness.html b/testing/web-platform/tests/FileAPI/idlharness.html new file mode 100644 index 0000000000..45e8684f00 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/idlharness.html @@ -0,0 +1,37 @@ + + + + + File API automated IDL tests (requiring dom) + + + + + + + + +

File API automated IDL tests

+ +
+ +
+ +
+ + + + + diff --git a/testing/web-platform/tests/FileAPI/idlharness.worker.js b/testing/web-platform/tests/FileAPI/idlharness.worker.js new file mode 100644 index 0000000000..002aaed40a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/idlharness.worker.js @@ -0,0 +1,17 @@ +importScripts("/resources/testharness.js"); +importScripts("/resources/WebIDLParser.js", "/resources/idlharness.js"); + +'use strict'; + +// https://w3c.github.io/FileAPI/ + +idl_test( + ['FileAPI'], + ['dom', 'html', 'url'], + idl_array => { + idl_array.add_objects({ + FileReaderSync: ['new FileReaderSync()'] + }); + } +); +done(); diff --git a/testing/web-platform/tests/FileAPI/progress-manual.html b/testing/web-platform/tests/FileAPI/progress-manual.html new file mode 100644 index 0000000000..b2e03b3eb2 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/progress-manual.html @@ -0,0 +1,49 @@ + + +Process Events for FileReader + + + + +Please choose one file through this input below.
+ +
+ diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/Determining-Encoding.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/Determining-Encoding.any.js new file mode 100644 index 0000000000..5b69f7ed98 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/Determining-Encoding.any.js @@ -0,0 +1,81 @@ +// META: title=FileAPI Test: Blob Determining Encoding + +var t = async_test("Blob Determing Encoding with encoding argument"); +t.step(function() { + // string 'hello' + var data = [0xFE,0xFF,0x00,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F]; + var blob = new Blob([new Uint8Array(data)]); + var reader = new FileReader(); + + reader.onloadend = t.step_func_done (function(event) { + assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16BE.") + }, reader); + + reader.readAsText(blob, "UTF-16BE"); +}); + +var t = async_test("Blob Determing Encoding with type attribute"); +t.step(function() { + var data = [0xFE,0xFF,0x00,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F]; + var blob = new Blob([new Uint8Array(data)], {type:"text/plain;charset=UTF-16BE"}); + var reader = new FileReader(); + + reader.onloadend = t.step_func_done (function(event) { + assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16BE.") + }, reader); + + reader.readAsText(blob); +}); + + +var t = async_test("Blob Determing Encoding with UTF-8 BOM"); +t.step(function() { + var data = [0xEF,0xBB,0xBF,0x68,0x65,0x6C,0x6C,0xC3,0xB6]; + var blob = new Blob([new Uint8Array(data)]); + var reader = new FileReader(); + + reader.onloadend = t.step_func_done (function(event) { + assert_equals(this.result, "hellö", "The FileReader should read the blob with UTF-8."); + }, reader); + + reader.readAsText(blob); +}); + +var t = async_test("Blob Determing Encoding without anything implying charset."); +t.step(function() { + var data = [0x68,0x65,0x6C,0x6C,0xC3,0xB6]; + var blob = new Blob([new Uint8Array(data)]); + var reader = new FileReader(); + + reader.onloadend = t.step_func_done (function(event) { + assert_equals(this.result, "hellö", "The FileReader should read the blob by default with UTF-8."); + }, reader); + + reader.readAsText(blob); +}); + +var t = async_test("Blob Determing Encoding with UTF-16BE BOM"); +t.step(function() { + var data = [0xFE,0xFF,0x00,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F]; + var blob = new Blob([new Uint8Array(data)]); + var reader = new FileReader(); + + reader.onloadend = t.step_func_done (function(event) { + assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16BE."); + }, reader); + + reader.readAsText(blob); +}); + +var t = async_test("Blob Determing Encoding with UTF-16LE BOM"); +t.step(function() { + var data = [0xFF,0xFE,0x68,0x00,0x65,0x00,0x6C,0x00,0x6C,0x00,0x6F,0x00]; + var blob = new Blob([new Uint8Array(data)]); + var reader = new FileReader(); + + reader.onloadend = t.step_func_done (function(event) { + assert_equals(this.result, "hello", "The FileReader should read the ArrayBuffer through UTF-16LE."); + }, reader); + + reader.readAsText(blob); +}); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-event-handler-attributes.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-event-handler-attributes.any.js new file mode 100644 index 0000000000..fc71c64348 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-event-handler-attributes.any.js @@ -0,0 +1,17 @@ +// META: title=FileReader event handler attributes + +var attributes = [ + "onloadstart", + "onprogress", + "onload", + "onabort", + "onerror", + "onloadend", +]; +attributes.forEach(function(a) { + test(function() { + var reader = new FileReader(); + assert_equals(reader[a], null, + "event handler attribute should initially be null"); + }, "FileReader." + a + ": initial value"); +}); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-multiple-reads.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-multiple-reads.any.js new file mode 100644 index 0000000000..4b19c69b42 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-multiple-reads.any.js @@ -0,0 +1,81 @@ +// META: title=FileReader: starting new reads while one is in progress + +test(function() { + var blob_1 = new Blob(['TEST000000001']) + var blob_2 = new Blob(['TEST000000002']) + var reader = new FileReader(); + reader.readAsText(blob_1) + assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") + assert_throws_dom("InvalidStateError", function () { + reader.readAsText(blob_2) + }) +}, 'test FileReader InvalidStateError exception for readAsText'); + +test(function() { + var blob_1 = new Blob(['TEST000000001']) + var blob_2 = new Blob(['TEST000000002']) + var reader = new FileReader(); + reader.readAsDataURL(blob_1) + assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") + assert_throws_dom("InvalidStateError", function () { + reader.readAsDataURL(blob_2) + }) +}, 'test FileReader InvalidStateError exception for readAsDataURL'); + +test(function() { + var blob_1 = new Blob(['TEST000000001']) + var blob_2 = new Blob(['TEST000000002']) + var reader = new FileReader(); + reader.readAsArrayBuffer(blob_1) + assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") + assert_throws_dom("InvalidStateError", function () { + reader.readAsArrayBuffer(blob_2) + }) +}, 'test FileReader InvalidStateError exception for readAsArrayBuffer'); + +async_test(function() { + var blob_1 = new Blob(['TEST000000001']) + var blob_2 = new Blob(['TEST000000002']) + var reader = new FileReader(); + var triggered = false; + reader.onloadstart = this.step_func_done(function() { + assert_false(triggered, "Only one loadstart event should be dispatched"); + triggered = true; + assert_equals(reader.readyState, FileReader.LOADING, + "readyState must be LOADING") + assert_throws_dom("InvalidStateError", function () { + reader.readAsArrayBuffer(blob_2) + }) + }); + reader.readAsArrayBuffer(blob_1) + assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") +}, 'test FileReader InvalidStateError exception in onloadstart event for readAsArrayBuffer'); + +async_test(function() { + var blob_1 = new Blob(['TEST000000001']) + var blob_2 = new Blob(['TEST000000002']) + var reader = new FileReader(); + reader.onloadend = this.step_func_done(function() { + assert_equals(reader.readyState, FileReader.DONE, + "readyState must be DONE") + reader.readAsArrayBuffer(blob_2) + assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") + }); + reader.readAsArrayBuffer(blob_1) + assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") +}, 'test FileReader no InvalidStateError exception in loadend event handler for readAsArrayBuffer'); + +async_test(function() { + var blob_1 = new Blob([new Uint8Array(0x414141)]); + var blob_2 = new Blob(['TEST000000002']); + var reader = new FileReader(); + reader.onloadstart = this.step_func(function() { + reader.abort(); + reader.onloadstart = null; + reader.onloadend = this.step_func_done(function() { + assert_equals('TEST000000002', reader.result); + }); + reader.readAsText(blob_2); + }); + reader.readAsText(blob_1); +}, 'test abort and restart in onloadstart event for readAsText'); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_abort.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_abort.any.js new file mode 100644 index 0000000000..c778ae55bb --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_abort.any.js @@ -0,0 +1,38 @@ +// META: title=FileAPI Test: filereader_abort + + test(function() { + var readerNoRead = new FileReader(); + readerNoRead.abort(); + assert_equals(readerNoRead.readyState, readerNoRead.EMPTY); + assert_equals(readerNoRead.result, null); + }, "Aborting before read"); + + promise_test(t => { + var blob = new Blob(["TEST THE ABORT METHOD"]); + var readerAbort = new FileReader(); + + var eventWatcher = new EventWatcher(t, readerAbort, + ['abort', 'loadstart', 'loadend', 'error', 'load']); + + // EventWatcher doesn't let us inspect the state after the abort event, + // so add an extra event handler for that. + readerAbort.addEventListener('abort', t.step_func(e => { + assert_equals(readerAbort.readyState, readerAbort.DONE); + })); + + readerAbort.readAsText(blob); + return eventWatcher.wait_for('loadstart') + .then(() => { + assert_equals(readerAbort.readyState, readerAbort.LOADING); + // 'abort' and 'loadend' events are dispatched synchronously, so + // call wait_for before calling abort. + var nextEvent = eventWatcher.wait_for(['abort', 'loadend']); + readerAbort.abort(); + return nextEvent; + }) + .then(() => { + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=24401 + assert_equals(readerAbort.result, null); + assert_equals(readerAbort.readyState, readerAbort.DONE); + }); + }, "Aborting after read"); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_error.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_error.any.js new file mode 100644 index 0000000000..9845962090 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_error.any.js @@ -0,0 +1,19 @@ +// META: title=FileAPI Test: filereader_error + + async_test(function() { + var blob = new Blob(["TEST THE ERROR ATTRIBUTE AND ERROR EVENT"]); + var reader = new FileReader(); + assert_equals(reader.error, null, "The error is null when no error occurred"); + + reader.onload = this.step_func(function(evt) { + assert_unreached("Should not dispatch the load event"); + }); + + reader.onloadend = this.step_func(function(evt) { + assert_equals(reader.result, null, "The result is null"); + this.done(); + }); + + reader.readAsText(blob); + reader.abort(); + }); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_events.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_events.any.js new file mode 100644 index 0000000000..ac692907d1 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_events.any.js @@ -0,0 +1,19 @@ +promise_test(async t => { + var reader = new FileReader(); + var eventWatcher = new EventWatcher(t, reader, ['loadstart', 'progress', 'abort', 'error', 'load', 'loadend']); + reader.readAsText(new Blob([])); + await eventWatcher.wait_for('loadstart'); + // No progress event for an empty blob, as no data is loaded. + await eventWatcher.wait_for('load'); + await eventWatcher.wait_for('loadend'); +}, 'events are dispatched in the correct order for an empty blob'); + +promise_test(async t => { + var reader = new FileReader(); + var eventWatcher = new EventWatcher(t, reader, ['loadstart', 'progress', 'abort', 'error', 'load', 'loadend']); + reader.readAsText(new Blob(['a'])); + await eventWatcher.wait_for('loadstart'); + await eventWatcher.wait_for('progress'); + await eventWatcher.wait_for('load'); + await eventWatcher.wait_for('loadend'); +}, 'events are dispatched in the correct order for a non-empty blob'); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_file-manual.html b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_file-manual.html new file mode 100644 index 0000000000..702ca9afd7 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_file-manual.html @@ -0,0 +1,69 @@ + + + + + FileAPI Test: filereader_file + + + + + + + +
+

Test step:

+
    +
  1. Download blue-100x100.png to local.
  2. +
  3. Select the local file (blue-100x100.png) to run the test.
  4. +
+
+ +
+ +
+ +
+ + + diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_file_img-manual.html b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_file_img-manual.html new file mode 100644 index 0000000000..fca42c7fce --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_file_img-manual.html @@ -0,0 +1,47 @@ + + + + + FileAPI Test: filereader_file_img + + + + + + + +
+

Test step:

+
    +
  1. Download blue-100x100.png to local.
  2. +
  3. Select the local file (blue-100x100.png) to run the test.
  4. +
+
+ +
+ +
+ +
+ + + diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsArrayBuffer.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsArrayBuffer.any.js new file mode 100644 index 0000000000..d06e317078 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsArrayBuffer.any.js @@ -0,0 +1,23 @@ +// META: title=FileAPI Test: filereader_readAsArrayBuffer + + async_test(function() { + var blob = new Blob(["TEST"]); + var reader = new FileReader(); + + reader.onload = this.step_func(function(evt) { + assert_equals(reader.result.byteLength, 4, "The byteLength is 4"); + assert_true(reader.result instanceof ArrayBuffer, "The result is instanceof ArrayBuffer"); + assert_equals(reader.readyState, reader.DONE); + this.done(); + }); + + reader.onloadstart = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.onprogress = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.readAsArrayBuffer(blob); + }); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsBinaryString.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsBinaryString.any.js new file mode 100644 index 0000000000..e69ff15e75 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsBinaryString.any.js @@ -0,0 +1,23 @@ +// META: title=FileAPI Test: filereader_readAsBinaryString + +async_test(t => { + const blob = new Blob(["σ"]); + const reader = new FileReader(); + + reader.onload = t.step_func_done(() => { + assert_equals(typeof reader.result, "string", "The result is string"); + assert_equals(reader.result.length, 2, "The result length is 2"); + assert_equals(reader.result, "\xcf\x83", "The result is \xcf\x83"); + assert_equals(reader.readyState, reader.DONE); + }); + + reader.onloadstart = t.step_func(() => { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.onprogress = t.step_func(() => { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.readAsBinaryString(blob); +}); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsDataURL.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsDataURL.any.js new file mode 100644 index 0000000000..4f9dbf7a75 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsDataURL.any.js @@ -0,0 +1,54 @@ +// META: title=FileAPI Test: FileReader.readAsDataURL + +async_test(function(testCase) { + var blob = new Blob(["TEST"]); + var reader = new FileReader(); + + reader.onload = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.DONE); + testCase.done(); + }); + reader.onloadstart = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING); + }); + reader.onprogress = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.readAsDataURL(blob); +}, 'FileReader readyState during readAsDataURL'); + +async_test(function(testCase) { + var blob = new Blob(["TEST"], { type: 'text/plain' }); + var reader = new FileReader(); + + reader.onload = this.step_func(function() { + assert_equals(reader.result, "data:text/plain;base64,VEVTVA=="); + testCase.done(); + }); + reader.readAsDataURL(blob); +}, 'readAsDataURL result for Blob with specified MIME type'); + +async_test(function(testCase) { + var blob = new Blob(["TEST"]); + var reader = new FileReader(); + + reader.onload = this.step_func(function() { + assert_equals(reader.result, + "data:application/octet-stream;base64,VEVTVA=="); + testCase.done(); + }); + reader.readAsDataURL(blob); +}, 'readAsDataURL result for Blob with unspecified MIME type'); + +async_test(function(testCase) { + var blob = new Blob([]); + var reader = new FileReader(); + + reader.onload = this.step_func(function() { + assert_equals(reader.result, + "data:application/octet-stream;base64,"); + testCase.done(); + }); + reader.readAsDataURL(blob); +}, 'readAsDataURL result for empty Blob'); \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsText.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsText.any.js new file mode 100644 index 0000000000..4d0fa11393 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readAsText.any.js @@ -0,0 +1,36 @@ +// META: title=FileAPI Test: filereader_readAsText + + async_test(function() { + var blob = new Blob(["TEST"]); + var reader = new FileReader(); + + reader.onload = this.step_func(function(evt) { + assert_equals(typeof reader.result, "string", "The result is typeof string"); + assert_equals(reader.result, "TEST", "The result is TEST"); + this.done(); + }); + + reader.onloadstart = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING, "The readyState"); + }); + + reader.onprogress = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.readAsText(blob); + }, "readAsText should correctly read UTF-8."); + + async_test(function() { + var blob = new Blob(["TEST"]); + var reader = new FileReader(); + var reader_UTF16 = new FileReader(); + reader_UTF16.onload = this.step_func(function(evt) { + // "TEST" in UTF-8 is 0x54 0x45 0x53 0x54. + // Decoded as utf-16 (little-endian), we get 0x4554 0x5453. + assert_equals(reader_UTF16.readyState, reader.DONE, "The readyState"); + assert_equals(reader_UTF16.result, "\u4554\u5453", "The result is not TEST"); + this.done(); + }); + reader_UTF16.readAsText(blob, "UTF-16"); + }, "readAsText should correctly read UTF-16."); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readystate.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readystate.any.js new file mode 100644 index 0000000000..3cb36ab999 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_readystate.any.js @@ -0,0 +1,19 @@ +// META: title=FileAPI Test: filereader_readystate + + async_test(function() { + var blob = new Blob(["THIS TEST THE READYSTATE WHEN READ BLOB"]); + var reader = new FileReader(); + + assert_equals(reader.readyState, reader.EMPTY); + + reader.onloadstart = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.LOADING); + }); + + reader.onloadend = this.step_func(function(evt) { + assert_equals(reader.readyState, reader.DONE); + this.done(); + }); + + reader.readAsDataURL(blob); + }); diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/filereader_result.any.js b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_result.any.js new file mode 100644 index 0000000000..28c068bb34 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/reading-data-section/filereader_result.any.js @@ -0,0 +1,82 @@ +// META: title=FileAPI Test: filereader_result + + var blob, blob2; + setup(function() { + blob = new Blob(["This test the result attribute"]); + blob2 = new Blob(["This is a second blob"]); + }); + + async_test(function() { + var readText = new FileReader(); + assert_equals(readText.result, null); + + readText.onloadend = this.step_func(function(evt) { + assert_equals(typeof readText.result, "string", "The result type is string"); + assert_equals(readText.result, "This test the result attribute", "The result is correct"); + this.done(); + }); + + readText.readAsText(blob); + }, "readAsText"); + + async_test(function() { + var readDataURL = new FileReader(); + assert_equals(readDataURL.result, null); + + readDataURL.onloadend = this.step_func(function(evt) { + assert_equals(typeof readDataURL.result, "string", "The result type is string"); + assert_true(readDataURL.result.indexOf("VGhpcyB0ZXN0IHRoZSByZXN1bHQgYXR0cmlidXRl") != -1, "return the right base64 string"); + this.done(); + }); + + readDataURL.readAsDataURL(blob); + }, "readAsDataURL"); + + async_test(function() { + var readArrayBuffer = new FileReader(); + assert_equals(readArrayBuffer.result, null); + + readArrayBuffer.onloadend = this.step_func(function(evt) { + assert_true(readArrayBuffer.result instanceof ArrayBuffer, "The result is instanceof ArrayBuffer"); + this.done(); + }); + + readArrayBuffer.readAsArrayBuffer(blob); + }, "readAsArrayBuffer"); + + async_test(function() { + var readBinaryString = new FileReader(); + assert_equals(readBinaryString.result, null); + + readBinaryString.onloadend = this.step_func(function(evt) { + assert_equals(typeof readBinaryString.result, "string", "The result type is string"); + assert_equals(readBinaryString.result, "This test the result attribute", "The result is correct"); + this.done(); + }); + + readBinaryString.readAsBinaryString(blob); + }, "readAsBinaryString"); + + + for (let event of ['loadstart', 'progress']) { + for (let method of ['readAsText', 'readAsDataURL', 'readAsArrayBuffer', 'readAsBinaryString']) { + promise_test(async function(t) { + var reader = new FileReader(); + assert_equals(reader.result, null, 'result is null before read'); + + var eventWatcher = new EventWatcher(t, reader, + [event, 'loadend']); + + reader[method](blob); + assert_equals(reader.result, null, 'result is null after first read call'); + await eventWatcher.wait_for(event); + assert_equals(reader.result, null, 'result is null during event'); + await eventWatcher.wait_for('loadend'); + assert_not_equals(reader.result, null); + reader[method](blob); + assert_equals(reader.result, null, 'result is null after second read call'); + await eventWatcher.wait_for(event); + assert_equals(reader.result, null, 'result is null during second read event'); + }, 'result is null during "' + event + '" event for ' + method); + } + } diff --git a/testing/web-platform/tests/FileAPI/reading-data-section/support/blue-100x100.png b/testing/web-platform/tests/FileAPI/reading-data-section/support/blue-100x100.png new file mode 100644 index 0000000000..5748719ff2 Binary files /dev/null and b/testing/web-platform/tests/FileAPI/reading-data-section/support/blue-100x100.png differ diff --git a/testing/web-platform/tests/FileAPI/support/Blob.js b/testing/web-platform/tests/FileAPI/support/Blob.js new file mode 100644 index 0000000000..2c24974685 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/Blob.js @@ -0,0 +1,70 @@ +'use strict' + +self.test_blob = (fn, expectations) => { + var expected = expectations.expected, + type = expectations.type, + desc = expectations.desc; + + var t = async_test(desc); + t.step(function() { + var blob = fn(); + assert_true(blob instanceof Blob); + assert_false(blob instanceof File); + assert_equals(blob.type, type); + assert_equals(blob.size, expected.length); + + var fr = new FileReader(); + fr.onload = t.step_func_done(function(event) { + assert_equals(this.result, expected); + }, fr); + fr.onerror = t.step_func(function(e) { + assert_unreached("got error event on FileReader"); + }); + fr.readAsText(blob, "UTF-8"); + }); +} + +self.test_blob_binary = (fn, expectations) => { + var expected = expectations.expected, + type = expectations.type, + desc = expectations.desc; + + var t = async_test(desc); + t.step(function() { + var blob = fn(); + assert_true(blob instanceof Blob); + assert_false(blob instanceof File); + assert_equals(blob.type, type); + assert_equals(blob.size, expected.length); + + var fr = new FileReader(); + fr.onload = t.step_func_done(function(event) { + assert_true(this.result instanceof ArrayBuffer, + "Result should be an ArrayBuffer"); + assert_array_equals(new Uint8Array(this.result), expected); + }, fr); + fr.onerror = t.step_func(function(e) { + assert_unreached("got error event on FileReader"); + }); + fr.readAsArrayBuffer(blob); + }); +} + +// Assert that two TypedArray objects have the same byte values +self.assert_equals_typed_array = (array1, array2) => { + const [view1, view2] = [array1, array2].map((array) => { + assert_true(array.buffer instanceof ArrayBuffer, + 'Expect input ArrayBuffers to contain field `buffer`'); + return new DataView(array.buffer, array.byteOffset, array.byteLength); + }); + + assert_equals(view1.byteLength, view2.byteLength, + 'Expect both arrays to be of the same byte length'); + + const byteLength = view1.byteLength; + + for (let i = 0; i < byteLength; ++i) { + assert_equals(view1.getUint8(i), view2.getUint8(i), + `Expect byte at buffer position ${i} to be equal`); + } +} diff --git a/testing/web-platform/tests/FileAPI/support/document-domain-setter.sub.html b/testing/web-platform/tests/FileAPI/support/document-domain-setter.sub.html new file mode 100644 index 0000000000..61aebdf326 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/document-domain-setter.sub.html @@ -0,0 +1,7 @@ + +Relevant/current/blob source page used as a test helper + + diff --git a/testing/web-platform/tests/FileAPI/support/empty-document.html b/testing/web-platform/tests/FileAPI/support/empty-document.html new file mode 100644 index 0000000000..b9cd130a07 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/empty-document.html @@ -0,0 +1,3 @@ + + + diff --git a/testing/web-platform/tests/FileAPI/support/historical-serviceworker.js b/testing/web-platform/tests/FileAPI/support/historical-serviceworker.js new file mode 100644 index 0000000000..8bd89a23ad --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/historical-serviceworker.js @@ -0,0 +1,5 @@ +importScripts('/resources/testharness.js'); + +test(() => { + assert_false('FileReaderSync' in self); +}, '"FileReaderSync" should not be supported in service workers'); diff --git a/testing/web-platform/tests/FileAPI/support/incumbent.sub.html b/testing/web-platform/tests/FileAPI/support/incumbent.sub.html new file mode 100644 index 0000000000..63a81cd328 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/incumbent.sub.html @@ -0,0 +1,22 @@ + +Incumbent page used as a test helper + + + + + + diff --git a/testing/web-platform/tests/FileAPI/support/send-file-form-helper.js b/testing/web-platform/tests/FileAPI/support/send-file-form-helper.js new file mode 100644 index 0000000000..d6adf21ec3 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/send-file-form-helper.js @@ -0,0 +1,282 @@ +'use strict'; + +// See /FileAPI/file/resources/echo-content-escaped.py +function escapeString(string) { + return string.replace(/\\/g, "\\\\").replace( + /[^\x20-\x7E]/g, + (x) => { + let hex = x.charCodeAt(0).toString(16); + if (hex.length < 2) hex = "0" + hex; + return `\\x${hex}`; + }, + ).replace(/\\x0d\\x0a/g, "\r\n"); +} + +// Rationale for this particular test character sequence, which is +// used in filenames and also in file contents: +// +// - ABC~ ensures the string starts with something we can read to +// ensure it is from the correct source; ~ is used because even +// some 1-byte otherwise-ASCII-like parts of ISO-2022-JP +// interpret it differently. +// - ‾¥ are inside a single-byte range of ISO-2022-JP and help +// diagnose problems due to filesystem encoding or locale +// - ≈ is inside IBM437 and helps diagnose problems due to filesystem +// encoding or locale +// - ¤ is inside Latin-1 and helps diagnose problems due to +// filesystem encoding or locale; it is also the "simplest" case +// needing substitution in ISO-2022-JP +// - ・ is inside a single-byte range of ISO-2022-JP in some variants +// and helps diagnose problems due to filesystem encoding or locale; +// on the web it is distinct when decoding but unified when encoding +// - ・ is inside a double-byte range of ISO-2022-JP and helps +// diagnose problems due to filesystem encoding or locale +// - • is inside Windows-1252 and helps diagnose problems due to +// filesystem encoding or locale and also ensures these aren't +// accidentally turned into e.g. control codes +// - ∙ is inside IBM437 and helps diagnose problems due to filesystem +// encoding or locale +// - · is inside Latin-1 and helps diagnose problems due to +// filesystem encoding or locale and also ensures HTML named +// character references (e.g. ·) are not used +// - ☼ is inside IBM437 shadowing C0 and helps diagnose problems due to +// filesystem encoding or locale and also ensures these aren't +// accidentally turned into e.g. control codes +// - ★ is inside ISO-2022-JP on a non-Kanji page and makes correct +// output easier to spot +// - 星 is inside ISO-2022-JP on a Kanji page and makes correct +// output easier to spot +// - 🌟 is outside the BMP and makes incorrect surrogate pair +// substitution detectable and ensures substitutions work +// correctly immediately after Kanji 2-byte ISO-2022-JP +// - 星 repeated here ensures the correct codec state is used +// after a non-BMP substitution +// - ★ repeated here also makes correct output easier to spot +// - ☼ is inside IBM437 shadowing C0 and helps diagnose problems due to +// filesystem encoding or locale and also ensures these aren't +// accidentally turned into e.g. control codes and also ensures +// substitutions work correctly immediately after non-Kanji +// 2-byte ISO-2022-JP +// - · is inside Latin-1 and helps diagnose problems due to +// filesystem encoding or locale and also ensures HTML named +// character references (e.g. ·) are not used +// - ∙ is inside IBM437 and helps diagnose problems due to filesystem +// encoding or locale +// - • is inside Windows-1252 and again helps diagnose problems +// due to filesystem encoding or locale +// - ・ is inside a double-byte range of ISO-2022-JP and helps +// diagnose problems due to filesystem encoding or locale +// - ・ is inside a single-byte range of ISO-2022-JP in some variants +// and helps diagnose problems due to filesystem encoding or locale; +// on the web it is distinct when decoding but unified when encoding +// - ¤ is inside Latin-1 and helps diagnose problems due to +// filesystem encoding or locale; again it is a "simple" +// substitution case +// - ≈ is inside IBM437 and helps diagnose problems due to filesystem +// encoding or locale +// - ¥‾ are inside a single-byte range of ISO-2022-JP and help +// diagnose problems due to filesystem encoding or locale +// - ~XYZ ensures earlier errors don't lead to misencoding of +// simple ASCII +// +// Overall the near-symmetry makes common I18N mistakes like +// off-by-1-after-non-BMP easier to spot. All the characters +// are also allowed in Windows Unicode filenames. +const kTestChars = 'ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ'; + +// The kTestFallback* strings represent the expected byte sequence from +// encoding kTestChars with the given encoding with "html" replacement +// mode, isomorphic-decoded. That means, characters that can't be +// encoded in that encoding get HTML-escaped, but no further +// `escapeString`-like escapes are needed. +const kTestFallbackUtf8 = ( + "ABC~\xE2\x80\xBE\xC2\xA5\xE2\x89\x88\xC2\xA4\xEF\xBD\xA5\xE3\x83\xBB\xE2" + + "\x80\xA2\xE2\x88\x99\xC2\xB7\xE2\x98\xBC\xE2\x98\x85\xE6\x98\x9F\xF0\x9F" + + "\x8C\x9F\xE6\x98\x9F\xE2\x98\x85\xE2\x98\xBC\xC2\xB7\xE2\x88\x99\xE2\x80" + + "\xA2\xE3\x83\xBB\xEF\xBD\xA5\xC2\xA4\xE2\x89\x88\xC2\xA5\xE2\x80\xBE~XYZ" +); + +const kTestFallbackIso2022jp = ( + ("ABC~\x1B(J~\\≈¤\x1B$B!&!&\x1B(B•∙·☼\x1B$B!z@1\x1B(B🌟" + + "\x1B$B@1!z\x1B(B☼·∙•\x1B$B!&!&\x1B(B¤≈\x1B(J\\~\x1B(B~XYZ") + .replace(/[^\0-\x7F]/gu, (x) => `&#${x.codePointAt(0)};`) +); + +const kTestFallbackWindows1252 = ( + "ABC~‾\xA5≈\xA4・・\x95∙\xB7☼★星🌟星★☼\xB7∙\x95・・\xA4≈\xA5‾~XYZ".replace( + /[^\0-\xFF]/gu, + (x) => `&#${x.codePointAt(0)};`, + ) +); + +const kTestFallbackXUserDefined = kTestChars.replace( + /[^\0-\x7F]/gu, + (x) => `&#${x.codePointAt(0)};`, +); + +// formPostFileUploadTest - verifies multipart upload structure and +// numeric character reference replacement for filenames, field names, +// and field values using form submission. +// +// Uses /FileAPI/file/resources/echo-content-escaped.py to echo the +// upload POST with controls and non-ASCII bytes escaped. This is done +// because navigations whose response body contains [\0\b\v] may get +// treated as a download, which is not what we want. Use the +// `escapeString` function to replicate that kind of escape (note that +// it takes an isomorphic-decoded string, not a byte sequence). +// +// Fields in the parameter object: +// +// - fileNameSource: purely explanatory and gives a clue about which +// character encoding is the source for the non-7-bit-ASCII parts of +// the fileBaseName, or Unicode if no smaller-than-Unicode source +// contains all the characters. Used in the test name. +// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename +// used for the constructed test file. Used in the test name. +// - formEncoding: the acceptCharset of the form used to submit the +// test file. Used in the test name. +// - expectedEncodedBaseName: the expected formEncoding-encoded +// version of fileBaseName, isomorphic-decoded. That means, characters +// that can't be encoded in that encoding get HTML-escaped, but no +// further `escapeString`-like escapes are needed. +const formPostFileUploadTest = ({ + fileNameSource, + fileBaseName, + formEncoding, + expectedEncodedBaseName, +}) => { + promise_test(async testCase => { + + if (document.readyState !== 'complete') { + await new Promise(resolve => addEventListener('load', resolve)); + } + + const formTargetFrame = Object.assign(document.createElement('iframe'), { + name: 'formtargetframe', + }); + document.body.append(formTargetFrame); + testCase.add_cleanup(() => { + document.body.removeChild(formTargetFrame); + }); + + const form = Object.assign(document.createElement('form'), { + acceptCharset: formEncoding, + action: '/FileAPI/file/resources/echo-content-escaped.py', + method: 'POST', + enctype: 'multipart/form-data', + target: formTargetFrame.name, + }); + document.body.append(form); + testCase.add_cleanup(() => { + document.body.removeChild(form); + }); + + // Used to verify that the browser agrees with the test about + // which form charset is used. + form.append(Object.assign(document.createElement('input'), { + type: 'hidden', + name: '_charset_', + })); + + // Used to verify that the browser agrees with the test about + // field value replacement and encoding independently of file system + // idiosyncracies. + form.append(Object.assign(document.createElement('input'), { + type: 'hidden', + name: 'filename', + value: fileBaseName, + })); + + // Same, but with name and value reversed to ensure field names + // get the same treatment. + form.append(Object.assign(document.createElement('input'), { + type: 'hidden', + name: fileBaseName, + value: 'filename', + })); + + const fileInput = Object.assign(document.createElement('input'), { + type: 'file', + name: 'file', + }); + form.append(fileInput); + + // Removes c:\fakepath\ or other pseudofolder and returns just the + // final component of filePath; allows both / and \ as segment + // delimiters. + const baseNameOfFilePath = filePath => filePath.split(/[\/\\]/).pop(); + await new Promise(resolve => { + const dataTransfer = new DataTransfer; + dataTransfer.items.add( + new File([kTestChars], fileBaseName, {type: 'text/plain'})); + fileInput.files = dataTransfer.files; + // For historical reasons .value will be prefixed with + // c:\fakepath\, but the basename should match the file name + // exposed through the newer .files[0].name API. This check + // verifies that assumption. + assert_equals( + baseNameOfFilePath(fileInput.files[0].name), + baseNameOfFilePath(fileInput.value), + `The basename of the field's value should match its files[0].name`); + form.submit(); + formTargetFrame.onload = resolve; + }); + + const formDataText = formTargetFrame.contentDocument.body.textContent; + const formDataLines = formDataText.split('\n'); + if (formDataLines.length && !formDataLines[formDataLines.length - 1]) { + --formDataLines.length; + } + assert_greater_than( + formDataLines.length, + 2, + `${fileBaseName}: multipart form data must have at least 3 lines: ${ + JSON.stringify(formDataText) + }`); + const boundary = formDataLines[0]; + assert_equals( + formDataLines[formDataLines.length - 1], + boundary + '--', + `${fileBaseName}: multipart form data must end with ${boundary}--: ${ + JSON.stringify(formDataText) + }`); + + const asValue = expectedEncodedBaseName.replace(/\r\n?|\n/g, "\r\n"); + const asName = asValue.replace(/[\r\n"]/g, encodeURIComponent); + const asFilename = expectedEncodedBaseName.replace(/[\r\n"]/g, encodeURIComponent); + + // The response body from echo-content-escaped.py has controls and non-ASCII + // bytes escaped, so any caller-provided field that might contain such bytes + // must be passed to `escapeString`, after any other expected + // transformations. + const expectedText = [ + boundary, + 'Content-Disposition: form-data; name="_charset_"', + '', + formEncoding, + boundary, + 'Content-Disposition: form-data; name="filename"', + '', + // Unlike for names and filenames, multipart/form-data values don't escape + // \r\n linebreaks, and when they're read from an iframe they become \n. + escapeString(asValue).replace(/\r\n/g, "\n"), + boundary, + `Content-Disposition: form-data; name="${escapeString(asName)}"`, + '', + 'filename', + boundary, + `Content-Disposition: form-data; name="file"; ` + + `filename="${escapeString(asFilename)}"`, + 'Content-Type: text/plain', + '', + escapeString(kTestFallbackUtf8), + boundary + '--', + ].join('\n'); + + assert_true( + formDataText.startsWith(expectedText), + `Unexpected multipart-shaped form data received:\n${ + formDataText + }\nExpected:\n${expectedText}`); + }, `Upload ${fileBaseName} (${fileNameSource}) in ${formEncoding} form`); +}; diff --git a/testing/web-platform/tests/FileAPI/support/send-file-formdata-helper.js b/testing/web-platform/tests/FileAPI/support/send-file-formdata-helper.js new file mode 100644 index 0000000000..53c8cca7e0 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/send-file-formdata-helper.js @@ -0,0 +1,99 @@ +"use strict"; + +const kTestChars = "ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ"; + +// formDataPostFileUploadTest - verifies multipart upload structure and +// numeric character reference replacement for filenames, field names, +// and field values using FormData and fetch(). +// +// Uses /fetch/api/resources/echo-content.py to echo the upload +// POST (unlike in send-file-form-helper.js, here we expect all +// multipart/form-data request bodies to be UTF-8, so we don't need to +// escape controls and non-ASCII bytes). +// +// Fields in the parameter object: +// +// - fileNameSource: purely explanatory and gives a clue about which +// character encoding is the source for the non-7-bit-ASCII parts of +// the fileBaseName, or Unicode if no smaller-than-Unicode source +// contains all the characters. Used in the test name. +// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename +// used for the constructed test file. Used in the test name. +const formDataPostFileUploadTest = ({ + fileNameSource, + fileBaseName, +}) => { + promise_test(async (testCase) => { + const formData = new FormData(); + let file = new Blob([kTestChars], { type: "text/plain" }); + try { + // Switch to File in browsers that allow this + file = new File([file], fileBaseName, { type: file.type }); + } catch (ignoredException) { + } + + // Used to verify that the browser agrees with the test about + // field value replacement and encoding independently of file system + // idiosyncracies. + formData.append("filename", fileBaseName); + + // Same, but with name and value reversed to ensure field names + // get the same treatment. + formData.append(fileBaseName, "filename"); + + formData.append("file", file, fileBaseName); + + const formDataText = await (await fetch( + `/fetch/api/resources/echo-content.py`, + { + method: "POST", + body: formData, + }, + )).text(); + const formDataLines = formDataText.split("\r\n"); + if (formDataLines.length && !formDataLines[formDataLines.length - 1]) { + --formDataLines.length; + } + assert_greater_than( + formDataLines.length, + 2, + `${fileBaseName}: multipart form data must have at least 3 lines: ${ + JSON.stringify(formDataText) + }`, + ); + const boundary = formDataLines[0]; + assert_equals( + formDataLines[formDataLines.length - 1], + boundary + "--", + `${fileBaseName}: multipart form data must end with ${boundary}--: ${ + JSON.stringify(formDataText) + }`, + ); + + const asValue = fileBaseName.replace(/\r\n?|\n/g, "\r\n"); + const asName = asValue.replace(/[\r\n"]/g, encodeURIComponent); + const asFilename = fileBaseName.replace(/[\r\n"]/g, encodeURIComponent); + const expectedText = [ + boundary, + 'Content-Disposition: form-data; name="filename"', + "", + asValue, + boundary, + `Content-Disposition: form-data; name="${asName}"`, + "", + "filename", + boundary, + `Content-Disposition: form-data; name="file"; ` + + `filename="${asFilename}"`, + "Content-Type: text/plain", + "", + kTestChars, + boundary + "--", + ].join("\r\n"); + + assert_true( + formDataText.startsWith(expectedText), + `Unexpected multipart-shaped form data received:\n${formDataText}\nExpected:\n${expectedText}`, + ); + }, `Upload ${fileBaseName} (${fileNameSource}) in fetch with FormData`); +}; diff --git a/testing/web-platform/tests/FileAPI/support/upload.txt b/testing/web-platform/tests/FileAPI/support/upload.txt new file mode 100644 index 0000000000..5ab2f8a432 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/upload.txt @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/support/url-origin.html b/testing/web-platform/tests/FileAPI/support/url-origin.html new file mode 100644 index 0000000000..6375511391 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/support/url-origin.html @@ -0,0 +1,6 @@ + + diff --git a/testing/web-platform/tests/FileAPI/unicode.html b/testing/web-platform/tests/FileAPI/unicode.html new file mode 100644 index 0000000000..ce3e3579d7 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/unicode.html @@ -0,0 +1,46 @@ + + +Blob/Unicode interaction: normalization and encoding + + + diff --git a/testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html b/testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html new file mode 100644 index 0000000000..ce9d680709 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html @@ -0,0 +1,62 @@ + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html b/testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html new file mode 100644 index 0000000000..0052b26fa6 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html @@ -0,0 +1,26 @@ + + +Blob URL serialization (specifically the origin) in multi-global situations + + + + + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/url/resources/create-helper.html b/testing/web-platform/tests/FileAPI/url/resources/create-helper.html new file mode 100644 index 0000000000..fa6cf4e671 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/resources/create-helper.html @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/url/resources/create-helper.js b/testing/web-platform/tests/FileAPI/url/resources/create-helper.js new file mode 100644 index 0000000000..e6344f700c --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/resources/create-helper.js @@ -0,0 +1,4 @@ +self.addEventListener('message', e => { + let url = URL.createObjectURL(e.data.blob); + self.postMessage({url: url}); +}); diff --git a/testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js b/testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js new file mode 100644 index 0000000000..a81ea1e7b1 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js @@ -0,0 +1,71 @@ +// This method generates a number of tests verifying fetching of blob URLs, +// allowing the same tests to be used both with fetch() and XMLHttpRequest. +// +// |fetch_method| is only used in test names, and should describe the +// (javascript) method being used by the other two arguments (i.e. 'fetch' or 'XHR'). +// +// |fetch_should_succeed| is a callback that is called with the Test and a URL. +// Fetching the URL is expected to succeed. The callback should return a promise +// resolved with whatever contents were fetched. +// +// |fetch_should_fail| similarly is a callback that is called with the Test, a URL +// to fetch, and optionally a method to use to do the fetch. If no method is +// specified the callback should use the 'GET' method. Fetching of these URLs is +// expected to fail, and the callback should return a promise that resolves iff +// fetching did indeed fail. +function fetch_tests(fetch_method, fetch_should_succeed, fetch_should_fail) { + const blob_contents = 'test blob contents'; + const blob = new Blob([blob_contents]); + + promise_test(t => { + const url = URL.createObjectURL(blob); + + return fetch_should_succeed(t, url).then(text => { + assert_equals(text, blob_contents); + }); + }, 'Blob URLs can be used in ' + fetch_method); + + promise_test(t => { + const url = URL.createObjectURL(blob); + + return fetch_should_succeed(t, url + '#fragment').then(text => { + assert_equals(text, blob_contents); + }); + }, fetch_method + ' with a fragment should succeed'); + + promise_test(t => { + const url = URL.createObjectURL(blob); + URL.revokeObjectURL(url); + + return fetch_should_fail(t, url); + }, fetch_method + ' of a revoked URL should fail'); + + promise_test(t => { + const url = URL.createObjectURL(blob); + URL.revokeObjectURL(url + '#fragment'); + + return fetch_should_succeed(t, url).then(text => { + assert_equals(text, blob_contents); + }); + }, 'Only exact matches should revoke URLs, using ' + fetch_method); + + promise_test(t => { + const url = URL.createObjectURL(blob); + + return fetch_should_fail(t, url + '?querystring'); + }, 'Appending a query string should cause ' + fetch_method + ' to fail'); + + promise_test(t => { + const url = URL.createObjectURL(blob); + + return fetch_should_fail(t, url + '/path'); + }, 'Appending a path should cause ' + fetch_method + ' to fail'); + + for (const method of ['HEAD', 'POST', 'DELETE', 'OPTIONS', 'PUT', 'CUSTOM']) { + const url = URL.createObjectURL(blob); + + promise_test(t => { + return fetch_should_fail(t, url, method); + }, fetch_method + ' with method "' + method + '" should fail'); + } +} \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html new file mode 100644 index 0000000000..adf5a014a6 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js new file mode 100644 index 0000000000..c3e05b64b1 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js @@ -0,0 +1,9 @@ +self.addEventListener('message', e => { + URL.revokeObjectURL(e.data.url); + // Registering a new object URL will make absolutely sure that the revocation + // has propagated. Without this at least in chrome it is possible for the + // below postMessage to arrive at its destination before the revocation has + // been fully processed. + URL.createObjectURL(new Blob([])); + self.postMessage('revoked'); +}); diff --git a/testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html b/testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html new file mode 100644 index 0000000000..a52939a3eb --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html @@ -0,0 +1,32 @@ + + +FileAPI Test: Verify behavior of Blob URL in unique origins + + + + + + + diff --git a/testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html b/testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html new file mode 100644 index 0000000000..2c4921c034 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html @@ -0,0 +1,23 @@ + + +FileAPI Test: Verify origin of Blob URL + + + + diff --git a/testing/web-platform/tests/FileAPI/url/url-charset.window.js b/testing/web-platform/tests/FileAPI/url/url-charset.window.js new file mode 100644 index 0000000000..777709b64a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-charset.window.js @@ -0,0 +1,34 @@ +async_test(t => { + // This could be detected as ISO-2022-JP, in which case there would be no + // bbb` + ], + {type: 'text/html;charset=utf-8'}); + const url = URL.createObjectURL(blob); + const win = window.open(url); + t.add_cleanup(() => { + win.close(); + }); + + win.onload = t.step_func_done(() => { + assert_equals(win.document.charset, 'UTF-8'); + }); +}, 'Blob charset should override any auto-detected charset.'); + +async_test(t => { + const blob = new Blob( + [`\n`], + {type: 'text/html;charset=utf-8'}); + const url = URL.createObjectURL(blob); + const win = window.open(url); + t.add_cleanup(() => { + win.close(); + }); + + win.onload = t.step_func_done(() => { + assert_equals(win.document.charset, 'UTF-8'); + }); +}, 'Blob charset should override .'); diff --git a/testing/web-platform/tests/FileAPI/url/url-format.any.js b/testing/web-platform/tests/FileAPI/url/url-format.any.js new file mode 100644 index 0000000000..69c51113e6 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-format.any.js @@ -0,0 +1,70 @@ +// META: timeout=long +const blob = new Blob(['test']); +const file = new File(['test'], 'name'); + +test(t => { + const url_count = 5000; + let list = []; + + t.add_cleanup(() => { + for (let url of list) { + URL.revokeObjectURL(url); + } + }); + + for (let i = 0; i < url_count; ++i) + list.push(URL.createObjectURL(blob)); + + list.sort(); + + for (let i = 1; i < list.length; ++i) + assert_not_equals(list[i], list[i-1], 'generated Blob URLs should be unique'); +}, 'Generated Blob URLs are unique'); + +test(() => { + const url = URL.createObjectURL(blob); + assert_equals(typeof url, 'string'); + assert_true(url.startsWith('blob:')); +}, 'Blob URL starts with "blob:"'); + +test(() => { + const url = URL.createObjectURL(file); + assert_equals(typeof url, 'string'); + assert_true(url.startsWith('blob:')); +}, 'Blob URL starts with "blob:" for Files'); + +test(() => { + const url = URL.createObjectURL(blob); + assert_equals(new URL(url).origin, location.origin); + if (location.origin !== 'null') { + assert_true(url.includes(location.origin)); + assert_true(url.startsWith('blob:' + location.protocol)); + } +}, 'Origin of Blob URL matches our origin'); + +test(() => { + const url = URL.createObjectURL(blob); + const url_record = new URL(url); + assert_equals(url_record.protocol, 'blob:'); + assert_equals(url_record.origin, location.origin); + assert_equals(url_record.host, '', 'host should be an empty string'); + assert_equals(url_record.port, '', 'port should be an empty string'); + const uuid_path_re = /\/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + assert_true(uuid_path_re.test(url_record.pathname), 'Path must end with a valid UUID'); + if (location.origin !== 'null') { + const nested_url = new URL(url_record.pathname); + assert_equals(nested_url.origin, location.origin); + assert_equals(nested_url.pathname.search(uuid_path_re), 0, 'Path must be a valid UUID'); + assert_true(url.includes(location.origin)); + assert_true(url.startsWith('blob:' + location.protocol)); + } +}, 'Blob URL parses correctly'); + +test(() => { + const url = URL.createObjectURL(file); + assert_equals(new URL(url).origin, location.origin); + if (location.origin !== 'null') { + assert_true(url.includes(location.origin)); + assert_true(url.startsWith('blob:' + location.protocol)); + } +}, 'Origin of Blob URL matches our origin for Files'); diff --git a/testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js b/testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js new file mode 100644 index 0000000000..1cdad79f7e --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js @@ -0,0 +1,115 @@ +// META: timeout=long +async_test(t => { + const run_result = 'test_frame_OK'; + const blob_contents = '\n\n' + + ''; + const blob = new Blob([blob_contents], {type: 'text/html'}); + const url = URL.createObjectURL(blob); + + const frame = document.createElement('iframe'); + frame.setAttribute('src', url); + frame.setAttribute('style', 'display:none;'); + document.body.appendChild(frame); + URL.revokeObjectURL(url); + + frame.onload = t.step_func_done(() => { + assert_equals(frame.contentWindow.test_result, run_result); + }); +}, 'Fetching a blob URL immediately before revoking it works in an iframe.'); + +async_test(t => { + const run_result = 'test_frame_OK'; + const blob_contents = '\n\n' + + ''; + const blob = new Blob([blob_contents], {type: 'text/html'}); + const url = URL.createObjectURL(blob); + + const frame = document.createElement('iframe'); + frame.setAttribute('src', '/common/blank.html'); + frame.setAttribute('style', 'display:none;'); + document.body.appendChild(frame); + + frame.onload = t.step_func(() => { + frame.contentWindow.location = url; + URL.revokeObjectURL(url); + frame.onload = t.step_func_done(() => { + assert_equals(frame.contentWindow.test_result, run_result); + }); + }); +}, 'Fetching a blob URL immediately before revoking it works in an iframe navigation.'); + +async_test(t => { + const run_result = 'test_frame_OK'; + const blob_contents = '\n\n' + + ''; + const blob = new Blob([blob_contents], {type: 'text/html'}); + const url = URL.createObjectURL(blob); + const win = window.open(url); + URL.revokeObjectURL(url); + add_completion_callback(() => { win.close(); }); + + win.onload = t.step_func_done(() => { + assert_equals(win.test_result, run_result); + }); +}, 'Opening a blob URL in a new window immediately before revoking it works.'); + +function receive_message_on_channel(t, channel_name) { + const channel = new BroadcastChannel(channel_name); + return new Promise(resolve => { + channel.addEventListener('message', t.step_func(e => { + resolve(e.data); + })); + }); +} + +function window_contents_for_channel(channel_name) { + return '\n' + + ''; +} + +async_test(t => { + const channel_name = 'noopener-window-test'; + const blob = new Blob([window_contents_for_channel(channel_name)], {type: 'text/html'}); + receive_message_on_channel(t, channel_name).then(t.step_func_done(t => { + assert_equals(t, 'foobar'); + })); + const url = URL.createObjectURL(blob); + const win = window.open(); + win.opener = null; + win.location = url; + URL.revokeObjectURL(url); +}, 'Opening a blob URL in a noopener about:blank window immediately before revoking it works.'); + +async_test(t => { + const run_result = 'test_script_OK'; + const blob_contents = 'window.script_test_result = "' + run_result + '";'; + const blob = new Blob([blob_contents]); + const url = URL.createObjectURL(blob); + + const e = document.createElement('script'); + e.setAttribute('src', url); + e.onload = t.step_func_done(() => { + assert_equals(window.script_test_result, run_result); + }); + + document.body.appendChild(e); + URL.revokeObjectURL(url); +}, 'Fetching a blob URL immediately before revoking it works in '; + const blob = new Blob([blob_contents], {type: 'text/html'}); + const url = URL.createObjectURL(blob); + + const frame = document.createElement('iframe'); + frame.setAttribute('src', url); + frame.setAttribute('style', 'display:none;'); + document.body.appendChild(frame); + + frame.onload = t.step_func_done(() => { + assert_equals(frame.contentWindow.test_result, run_result); + }); +}, 'Blob URLs can be used in iframes, and are treated same origin'); + +async_test(t => { + const blob_contents = '\n\n' + + '\n' + + '\n' + + '
\n' + + '
'; + const blob = new Blob([blob_contents], {type: 'text/html'}); + const url = URL.createObjectURL(blob); + + const frame = document.createElement('iframe'); + frame.setAttribute('src', url + '#block2'); + document.body.appendChild(frame); + frame.contentWindow.onscroll = t.step_func_done(() => { + assert_equals(frame.contentWindow.scrollY, 5000); + }); +}, 'Blob URL fragment is implemented.'); diff --git a/testing/web-platform/tests/FileAPI/url/url-lifetime.html b/testing/web-platform/tests/FileAPI/url/url-lifetime.html new file mode 100644 index 0000000000..ad5d667193 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-lifetime.html @@ -0,0 +1,56 @@ + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/FileAPI/url/url-reload.window.js b/testing/web-platform/tests/FileAPI/url/url-reload.window.js new file mode 100644 index 0000000000..d333b3a74a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-reload.window.js @@ -0,0 +1,36 @@ +function blob_url_reload_test(t, revoke_before_reload) { + const run_result = 'test_frame_OK'; + const blob_contents = '\n\n' + + ''; + const blob = new Blob([blob_contents], {type: 'text/html'}); + const url = URL.createObjectURL(blob); + + const frame = document.createElement('iframe'); + frame.setAttribute('src', url); + frame.setAttribute('style', 'display:none;'); + document.body.appendChild(frame); + + frame.onload = t.step_func(() => { + if (revoke_before_reload) + URL.revokeObjectURL(url); + assert_equals(frame.contentWindow.test_result, run_result); + frame.contentWindow.test_result = null; + frame.onload = t.step_func_done(() => { + assert_equals(frame.contentWindow.test_result, run_result); + }); + // Slight delay before reloading to ensure revoke actually has had a chance + // to be processed. + t.step_timeout(() => { + frame.contentWindow.location.reload(); + }, 250); + }); +} + +async_test(t => { + blob_url_reload_test(t, false); +}, 'Reloading a blob URL succeeds.'); + + +async_test(t => { + blob_url_reload_test(t, true); +}, 'Reloading a blob URL succeeds even if the URL was revoked.'); diff --git a/testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js b/testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js new file mode 100644 index 0000000000..54e6a3da5a --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js @@ -0,0 +1,72 @@ +// META: script=resources/fetch-tests.js +// META: script=/common/gc.js + +function fetch_should_succeed(test, request) { + return fetch(request).then(response => response.text()); +} + +function fetch_should_fail(test, url, method = 'GET') { + return promise_rejects_js(test, TypeError, fetch(url, {method: method})); +} + +fetch_tests('fetch', fetch_should_succeed, fetch_should_fail); + +promise_test(t => { + const blob_contents = 'test blob contents'; + const blob_type = 'image/png'; + const blob = new Blob([blob_contents], {type: blob_type}); + const url = URL.createObjectURL(blob); + + return fetch(url).then(response => { + assert_equals(response.headers.get('Content-Type'), blob_type); + }); +}, 'fetch should return Content-Type from Blob'); + +promise_test(t => { + const blob_contents = 'test blob contents'; + const blob = new Blob([blob_contents]); + const url = URL.createObjectURL(blob); + const request = new Request(url); + + // Revoke the object URL. Request should take a reference to the blob as + // soon as it receives it in open(), so the request succeeds even though we + // revoke the URL before calling fetch(). + URL.revokeObjectURL(url); + + return fetch_should_succeed(t, request).then(text => { + assert_equals(text, blob_contents); + }); +}, 'Revoke blob URL after creating Request, will fetch'); + +promise_test(async t => { + const blob_contents = 'test blob contents'; + const blob = new Blob([blob_contents]); + const url = URL.createObjectURL(blob); + let request = new Request(url); + + // Revoke the object URL. Request should take a reference to the blob as + // soon as it receives it in open(), so the request succeeds even though we + // revoke the URL before calling fetch(). + URL.revokeObjectURL(url); + + request = request.clone(); + await garbageCollect(); + + const text = await fetch_should_succeed(t, request); + assert_equals(text, blob_contents); +}, 'Revoke blob URL after creating Request, then clone Request, will fetch'); + +promise_test(function(t) { + const blob_contents = 'test blob contents'; + const blob = new Blob([blob_contents]); + const url = URL.createObjectURL(blob); + + const result = fetch_should_succeed(t, url).then(text => { + assert_equals(text, blob_contents); + }); + + // Revoke the object URL. fetch should have already resolved the blob URL. + URL.revokeObjectURL(url); + + return result; +}, 'Revoke blob URL after calling fetch, fetch should succeed'); diff --git a/testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js b/testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js new file mode 100644 index 0000000000..29d83080ab --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js @@ -0,0 +1,68 @@ +// META: script=resources/fetch-tests.js + +function xhr_should_succeed(test, url) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onload = test.step_func(() => { + assert_equals(xhr.status, 200); + assert_equals(xhr.statusText, 'OK'); + resolve(xhr.response); + }); + xhr.onerror = () => reject('Got unexpected error event'); + xhr.send(); + }); +} + +function xhr_should_fail(test, url, method = 'GET') { + const xhr = new XMLHttpRequest(); + xhr.open(method, url); + const result1 = new Promise((resolve, reject) => { + xhr.onload = () => reject('Got unexpected load event'); + xhr.onerror = resolve; + }); + const result2 = new Promise(resolve => { + xhr.onreadystatechange = test.step_func(() => { + if (xhr.readyState !== xhr.DONE) return; + assert_equals(xhr.status, 0); + resolve(); + }); + }); + xhr.send(); + return Promise.all([result1, result2]); +} + +fetch_tests('XHR', xhr_should_succeed, xhr_should_fail); + +async_test(t => { + const blob_contents = 'test blob contents'; + const blob_type = 'image/png'; + const blob = new Blob([blob_contents], {type: blob_type}); + const url = URL.createObjectURL(blob); + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onloadend = t.step_func_done(() => { + assert_equals(xhr.getResponseHeader('Content-Type'), blob_type); + }); + xhr.send(); +}, 'XHR should return Content-Type from Blob'); + +async_test(t => { + const blob_contents = 'test blob contents'; + const blob = new Blob([blob_contents]); + const url = URL.createObjectURL(blob); + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + + // Revoke the object URL. XHR should take a reference to the blob as soon as + // it receives it in open(), so the request succeeds even though we revoke the + // URL before calling send(). + URL.revokeObjectURL(url); + + xhr.onload = t.step_func_done(() => { + assert_equals(xhr.response, blob_contents); + }); + xhr.onerror = t.unreached_func('Got unexpected error event'); + + xhr.send(); +}, 'Revoke blob URL after open(), will fetch'); diff --git a/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html new file mode 100644 index 0000000000..7ae32512e0 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html @@ -0,0 +1,45 @@ + + +FileAPI Test: Creating Blob URL with File + + + + + + +
+

Test steps:

+
    +
  1. Download blue96x96.png to local.
  2. +
  3. Select the local file (blue96x96.png) to run the test.
  4. +
+
+ +
+ +
+ +
+ + + diff --git a/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html new file mode 100644 index 0000000000..534c1de996 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html @@ -0,0 +1,28 @@ + + +FileAPI Test: Creating Blob URL with File as image source + + + +
+

Test steps:

+
    +
  1. Download blue96x96.png to local.
  2. +
  3. Select the local file (blue96x96.png) to run the test.
  4. +
+

Pass/fail criteria:

+

Test passes if there is a filled blue square.

+ +

+

+
+ + + diff --git a/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html new file mode 100644 index 0000000000..7d7390442d --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html @@ -0,0 +1,12 @@ + + +FileAPI Reference File + + + +

Test passes if there is a filled blue square.

+ +

+ +

+ diff --git a/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html new file mode 100644 index 0000000000..468dcb086d --- /dev/null +++ b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html @@ -0,0 +1,27 @@ + + + +FileAPI Test: Creating Blob URL via XMLHttpRequest as image source + + + + +

Test passes if there is a filled blue square.

+ +

+ +

+ + + + -- cgit v1.2.3